-
#------------------------------------------------------------------------------
-
#
-
# Base class for all TransAM calculators. Each instance of this class must provide
-
# a calculate(asset) method.
-
#
-
#------------------------------------------------------------------------------
-
-
1
class Calculator
-
-
# Include the fiscal year mixin
-
1
include FiscalYear
-
-
1
INFINITY = 999999999.9
-
-
# All class instances should override this method to return a calculated value for the asset
-
1
def calculate(asset)
-
nil
-
end
-
-
end
-
1
class ConditionEstimationCalculator < Calculator
-
-
# determine the slope between two points
-
1
def slope(x1, y1, x2, y2)
-
25
dx = x2 - x1
-
25
dy = y2 - y1
-
-
25
if dy.abs < 0.0001
-
2
0.0
-
23
elsif dx.abs < 0.0001
-
5
-1.0
-
else
-
18
dy.to_f / dx.to_f
-
end
-
end
-
-
end
-
#------------------------------------------------------------------------------
-
#
-
# Base class for calculating asset costs
-
#
-
#------------------------------------------------------------------------------
-
1
class CostCalculator < Calculator
-
-
1
protected
-
-
# Returns the replacement year for the asset. This is either the schedule_replacement_year if set
-
# or the policy_replacement_year otherwise
-
# replacement year is in FY
-
1
def replacement_year(asset)
-
2
asset.scheduled_replacement_year.blank? ? asset.policy_replacement_year : asset.scheduled_replacement_year
-
end
-
-
# future cost calculation. calculates cost plus compounded interest over the number of years
-
1
def future_cost(initial_cost, number_of_years, interest_rate)
-
7
initial_cost * (1 + interest_rate) ** number_of_years
-
end
-
-
end
-
1
class CustomWeightedConditionRollupCalculator < Calculator
-
-
# Calculates the last year for service given the asset sub type and the date the asset was placed
-
# into service
-
1
def calculate(asset)
-
-
2
Rails.logger.debug "CustomWeightedConditionRollupCalculator.calculate(asset)"
-
-
2
ratings = asset.dependents.where('weight IS NOT NULL').pluck(:weight, :reported_condition_rating)
-
2
return nil if ratings.size == 0
-
-
1
temp_sum = 0
-
1
ratings.each do |rc|
-
3
temp_sum += rc[0] * (rc[1] || 0) # if no reported condition, default to 0 / Unknown
-
end
-
-
4
return (temp_sum/(ratings.sum{|rc| rc[0]})).round # for now round
-
-
end
-
-
end
-
1
class MedianConditionRollupCalculator < Calculator
-
-
# Calculates the last year for service given the asset sub type and the date the asset was placed
-
# into service
-
1
def calculate(asset)
-
-
2
Rails.logger.debug "MedianConditionRollupCalculator.calculate(asset)"
-
-
2
ratings = asset.dependents.pluck(:reported_condition_rating)
-
2
return nil if ratings.size == 0
-
-
1
tmp = ratings.sort
-
1
mid = (tmp.size / 2).to_i
-
1
return tmp[mid]
-
-
end
-
-
end
-
#------------------------------------------------------------------------------
-
#
-
# PurchasePricePlusInterestCalculator
-
#
-
# Calculates asset costs base on the initial price of the asset plus compounded
-
# interest from the in service year (in FY) to the replacement year (in FY)
-
#
-
#------------------------------------------------------------------------------
-
1
class PurchasePricePlusInterestCalculator < CostCalculator
-
-
1
def calculate(asset)
-
1
calculate_on_date(asset)
-
end
-
-
1
def calculate_on_date(asset, on_date=nil)
-
-
2
Rails.logger.debug "PurchasePricePlusInterestCalculator.calculate(asset)"
-
-
# Get the initial cost of the asset
-
2
initial_cost = asset.cost
-
2
Rails.logger.debug "initial_cost #{initial_cost}"
-
-
2
purchase_date = asset.purchase_date
-
-
2
if on_date.nil?
-
1
num_years_to_replacement = [current_planning_year_year - fiscal_year_year_on_date(purchase_date), 0].max
-
else
-
1
num_years_to_replacement = [fiscal_year_year_on_date(on_date)- fiscal_year_year_on_date(purchase_date), 0].max
-
end
-
2
Rails.logger.debug "num_years_to_replacement #{num_years_to_replacement}"
-
-
# inflation rate as decimal
-
2
inflation_rate = asset.policy_analyzer.get_annual_inflation_rate / 100.0
-
2
cost_in_future = future_cost(initial_cost, num_years_to_replacement, inflation_rate)
-
2
(cost_in_future + 0.5).floor # even rounds up
-
end
-
-
end
-
#------------------------------------------------------------------------------
-
#
-
# RehabilitationYearCalculator
-
#
-
# Returns the year that an asset should be rehabilitated based on the asset
-
# and the asset's policy
-
#
-
#------------------------------------------------------------------------------
-
1
class RehabilitationYearCalculator < Calculator
-
-
# Main entry point
-
1
def calculate(asset)
-
-
6
Rails.logger.debug "RehabilitationYearCalculator.calculate(asset)"
-
# The policy rehabilitation month identifies the month of the assets service
-
# that a rehabiliation should be performed. If not set, no rehabiliation
-
# will be scheduled otherwise find the fiscal year that is x months after the
-
# fiscal year the asset was placed in service
-
6
months_into_service = asset.policy_analyzer.get_rehabilitation_service_month
-
6
if months_into_service.to_i > 0
-
# return the in service fiscal year plus the policy rehab year
-
1
fiscal_year_year_on_date(asset.in_service_date) + (months_into_service / 12)
-
else
-
nil
-
end
-
end
-
-
1
protected
-
-
end
-
#------------------------------------------------------------------------------
-
#
-
# ReplacementCostCalculator
-
#
-
#------------------------------------------------------------------------------
-
1
class ReplacementCostCalculator < CostCalculator
-
-
# Determines the calculated replacement cost for an asset.
-
1
def calculate(asset)
-
-
# The default behavior is to return the replacement cost of the asset from the schedule
-
11
asset.policy_analyzer.get_replacement_cost
-
-
end
-
-
# As we are using the schedule, there is no difference using the date so we
-
# simply delegate to the calculate method
-
1
def calculate_on_date(asset,on_date=nil)
-
10
calculate(asset)
-
end
-
-
end
-
#------------------------------------------------------------------------------
-
#
-
# ReplacementCostPlusInterestCalculator
-
#
-
#------------------------------------------------------------------------------
-
1
class ReplacementCostPlusInterestCalculator < ReplacementCostCalculator
-
-
1
def calculate(asset)
-
1
calculate_on_date(asset)
-
end
-
-
# average replacement cost for the asset plus accrued interest
-
# from the policy year (in FY) until the FY of the given date (the planning year by default)
-
1
def calculate_on_date(asset,on_date=nil)
-
-
2
Rails.logger.debug "ReplacementCostPlusInterestCalculator.calculate(asset)"
-
-
# Get the replacement cost of the asset
-
2
replacement_cost = asset.policy_analyzer.get_replacement_cost
-
2
Rails.logger.debug "initial_cost #{replacement_cost}"
-
-
2
if on_date.nil?
-
1
replacement_year = current_planning_year_year
-
else
-
1
replacement_year = fiscal_year_year_on_date(on_date)
-
end
-
2
Rails.logger.debug "actual replacement_year #{replacement_year}"
-
-
# Calculate the number of years to carry the inflation rate
-
2
num_years_to_replacement = [replacement_year - asset.policy_analyzer.get_cost_fy_year, 0].max
-
2
Rails.logger.debug "num_years_to_replacement #{num_years_to_replacement}"
-
-
# interest rate as decimal
-
2
inflation_rate = asset.policy_analyzer.get_annual_inflation_rate / 100.0
-
-
2
future_cost(replacement_cost, num_years_to_replacement, inflation_rate)
-
end
-
-
-
end
-
#------------------------------------------------------------------------------
-
#
-
# ServiceLifeAgeAndCondition
-
#
-
#------------------------------------------------------------------------------
-
1
class ServiceLifeAgeAndCondition < ServiceLifeCalculator
-
-
# Calculates the last year for service based on the maximum of the average asset
-
# service life or the condition
-
1
def calculate(asset)
-
-
1
Rails.logger.debug "ServiceLifeAgeAndCondition.calculate(asset)"
-
-
# get the expected last year of service based on age
-
1
last_year_by_age = by_age(asset)
-
-
# get the predicted last year of service based on the asset condition
-
1
last_year_by_condition = by_condition(asset)
-
-
# return the minimum of the two
-
1
[last_year_by_age, last_year_by_condition].max
-
end
-
-
end
-
#------------------------------------------------------------------------------
-
#
-
# ServiceLifeAgeOnly
-
#
-
#------------------------------------------------------------------------------
-
1
class ServiceLifeAgeOnly < ServiceLifeCalculator
-
-
# Calculates the last year for service given the asset sub type and the date the asset was placed
-
# into service
-
1
def calculate(asset)
-
-
4
Rails.logger.debug "ServiceLifeAgeOnly.calculate(asset)"
-
-
4
by_age(asset)
-
-
end
-
-
end
-
#------------------------------------------------------------------------------
-
#
-
# ServiceLifeAgeOr`Condition
-
#
-
#------------------------------------------------------------------------------
-
1
class ServiceLifeAgeOrCondition < ServiceLifeCalculator
-
-
# Calculates the last year for service based on the minimum of the average asset
-
# service life or the condition
-
1
def calculate(asset)
-
-
1
Rails.logger.debug "ServiceLifeAgeAndCondition.calculate(asset)"
-
-
# get the expected last year of service based on age
-
1
last_year_by_age = by_age(asset)
-
-
# get the predicted last year of service based on the asset condition
-
1
last_year_by_condition = by_condition(asset)
-
-
# return the minimum of the two
-
1
[last_year_by_age, last_year_by_condition].min
-
end
-
-
end
-
#------------------------------------------------------------------------------
-
#
-
# ServiceLifeCalculator
-
#
-
# base class for ServiceLife calculators
-
#
-
#------------------------------------------------------------------------------
-
1
class ServiceLifeCalculator < Calculator
-
-
1
protected
-
-
# calculate the service life based on the in_service_date FY plus the minimum
-
# espected life of the asset. If the asset has been rehabilitated we factor
-
# in any increase in longevity based on the current policy
-
1
def by_age(asset)
-
-
11
year = fiscal_year_year_on_date(asset.in_service_date) + (asset.expected_useful_life / 12)
-
11
if asset.last_rehabilitation_date.present?
-
asset.rehabilitation_updates.each do |evt|
-
year += (evt.extended_useful_life_months / 12)
-
end
-
end
-
11
year
-
-
end
-
-
# Calculate the service life based on the minimum of condition
-
# returns in FY
-
1
def by_condition(asset)
-
# Iterate over all the condition update events from earliest to latest
-
# and find the first year (if any) that the policy replacement became
-
# effective
-
6
events = asset.condition_updates.reload
-
6
condition_threshold = asset.policy_analyzer.get_condition_threshold
-
6
Rails.logger.debug "Found #{events.count} events."
-
6
Rails.logger.debug "Condition threshold = #{condition_threshold}."
-
6
events.each do |event|
-
6
Rails.logger.debug "Event date = #{event.event_date}, Rating = #{event.assessed_rating}."
-
6
if event.assessed_rating <= condition_threshold
-
3
Rails.logger.debug "returning #{fiscal_year_year_on_date(event.event_date)}"
-
3
return fiscal_year_year_on_date(event.event_date)
-
end
-
end
-
# if we didn't find a condition event that would make the policy effective
-
# we can simply return the age constraint
-
3
Rails.logger.debug "returning value from policy age"
-
-
3
last_year_by_age = by_age(asset)
-
-
3
if last_year_by_age <= fiscal_year_year_on_date(Date.today) + 1
-
2
fiscal_year_year_on_date(Date.today) + 2
-
else
-
1
last_year_by_age
-
end
-
end
-
-
end
-
#------------------------------------------------------------------------------
-
#
-
# ServiceLifeConditionOnly
-
#
-
#------------------------------------------------------------------------------
-
1
class ServiceLifeConditionOnly < ServiceLifeCalculator
-
-
# Calculates the last year for service based on the condition of the asset
-
1
def calculate(asset)
-
-
1
Rails.logger.debug "ServiceLifeConditionOnly.calculate(asset)"
-
-
1
by_condition(asset)
-
end
-
-
end
-
# This calculator is not used and is broken.
-
# It estimates the slope for a straight line estimation based on the last condition update ONLY. It doesn't take into account all the updates and therefore can reflect a very inaccurate slope if the last update is vatly different from the rest.
-
# We currently use the Google Chart API that has trendlines of scatter plots.
-
-
#------------------------------------------------------------------------------
-
#
-
# StraightLineEstimationCalculator
-
#
-
#------------------------------------------------------------------------------
-
1
class StraightLineEstimationCalculator < ConditionEstimationCalculator
-
-
# Estimates the condition rating of an asset using straight-line depreciation
-
# based on
-
#
-
# 1] a measured rate of change if the asset has one or more condition updates, or
-
# 2] the expected rate of change if no updates have been posted against the asset
-
#
-
# If the asset has mileage reported then the measured rate of change will be the minimum
-
# of the condition assement or milege assessment
-
#
-
1
def calculate(asset)
-
8
calculate_on_date(asset)
-
end
-
-
1
def calculate_on_date(asset,on_date=nil)
-
-
8
Rails.logger.debug "StraightLineEstimationCalculator.calculate(asset)"
-
-
# get the maximum and minimum rating for a new asset
-
8
max_rating = ConditionType.max_rating # Usually 5.0 for FTA applicaitons
-
8
min_rating = ConditionType.min_rating # Usually 1.0 for FTA applications
-
-
# this is the rating that indicates the asset is at the end of its useful life. Usually 2.5 for FTA applications
-
8
condition_threshold = asset.policy_analyzer.get_condition_threshold
-
-
# determine the minimum estimated rating, this is the one with the most negative slope
-
8
slopes = calculate_slope(asset, min_rating, max_rating)
-
-
# determine the scale factor to make the mileage in the same interval as condition
-
8
scale_factor = 1.0
-
8
max_service_life_miles = asset.policy_analyzer.get_max_service_life_miles
-
8
unless max_service_life_miles.nil?
-
if max_service_life_miles > 0
-
scale_factor = (max_rating - condition_threshold) / max_service_life_miles
-
end
-
end
-
-
8
min_slope = [slopes[:condition_slope], slopes[:mileage_slope] * scale_factor].min
-
8
if on_date.nil?
-
8
est_rating = max_rating + (min_slope * asset.age)
-
else
-
est_rating = max_rating + (min_slope * asset.age(on_date))
-
end
-
8
Rails.logger.debug "est rating = #{est_rating}"
-
# make sure we don't go below the minimum. This is possible as the slope can extend infinitely for extreme cases
-
8
[est_rating.round(2), min_rating].max
-
end
-
-
# Estimates the last servicable year for the asset based on the last reported condition. If no
-
# condition has been reported, the policy year is returned
-
# returned in FY
-
1
def last_servicable_year(asset)
-
-
7
Rails.logger.debug "StraightLineEstimationCalculator.last_servicable_year(asset)"
-
-
# get the maximum and minimum rating for a new asset
-
7
max_rating = ConditionType.max_rating # Usually 5.0 for FTA applicaitons
-
7
min_rating = ConditionType.min_rating # Usually 1.0 for FTA applications
-
-
# this is the rating that indicates the asset is at the end of its useful life. Usually 2.5 for FTA applications
-
7
condition_threshold = asset.policy_analyzer.get_condition_threshold
-
-
# get max service life in months
-
7
max_service_life_months = asset.policy_analyzer.get_max_service_life_miles
-
-
7
years_policy = asset.expected_useful_life.nil? ? max_service_life_months / 12.0 : asset.expected_useful_life / 12.0
-
7
years_mileage = INFINITY
-
7
years_condition = INFINITY
-
-
# Using the slope intercept formula y = mx + b
-
# where m is the slope
-
# b in the y intercept (max_rating)
-
# we can solve for x (year) when we know y (condition) by re-arranging to
-
# x = (y - b) / m
-
-
7
slopes = calculate_slope(asset, min_rating, max_rating)
-
# condition
-
7
m = slopes[:condition_slope]
-
7
if m.abs.between?(0.00001, 0.99999)
-
5
b = max_rating
-
5
y = condition_threshold
-
5
Rails.logger.debug "y = mx + b => #{y} = #{m}x + #{b}"
-
5
x = (y - b) / m
-
5
Rails.logger.debug "x = (y - b) / m => #{x} = (#{y} - #{b}) + #{m}"
-
# Take care of any rounding errors
-
5
years_condition = (x + 0.1).to_i
-
end
-
-
# mileage
-
7
max_service_life_miles = asset.policy_analyzer.get_max_service_life_miles
-
7
unless max_service_life_miles.nil?
-
m = slopes[:mileage_slope]
-
if m.abs.between?(0.00001, 0.99999)
-
b = max_service_life_miles
-
y = 0
-
x = (y - b) / m
-
# Take care of any rounding errors
-
years_mileage = (x + 0.1).to_i
-
end
-
end
-
-
# return the last year that the asset is viable
-
7
Rails.logger.debug "years_policy = #{years_policy}, years_mileage = #{years_mileage}, years_condition = #{years_condition}"
-
7
fiscal_year_year_on_date(asset.in_service_date) + [years_policy, years_mileage, years_condition].min
-
end
-
-
1
protected
-
1
def calculate_slope(asset, min_rating, max_rating)
-
#
-
# We calculate the slope (y2 - y1) / (x2 - x2) where the x axis is the years and the y axis is the rating
-
# point (x1, y1) is the new asset (0 years, max_condition)
-
# point (x2, y2) is the last known data point if we have one or the end of useful life determined by
-
# the policy if we dont
-
-
# slope should always be negative as an asset's rating lowers over time
-
-
# this is the rating that indicates the asset is at the end of its useful life. Usually 2.5 for FTA applications
-
15
condition_threshold = asset.policy.condition_threshold
-
-
# Get what we need from the policy
-
15
max_service_life_months = asset.policy_analyzer.get_max_service_life_months
-
15
max_service_life_miles = asset.policy_analyzer.get_max_service_life_miles
-
-
# this is the max number of years for the asset, i.e. the number of years before the asset's rating is expected to
-
# reach the condition_threshold value
-
15
years_policy = asset.expected_useful_life.nil? ? max_service_life_months / 12.0 : asset.expected_useful_life / 12.0
-
-
15
Rails.logger.debug "asset.age=#{asset.age}, threshold=#{condition_threshold}, min_rating=#{min_rating}, max_rating=#{max_rating}"
-
-
# Assets always rated at the max value when they are new
-
15
x1 = 0
-
15
y1 = max_rating
-
-
# we might calculate this later if we have data
-
15
mileage_slope = 0.0
-
-
# If there are no condition updates then just return the policy based
-
# estimation
-
15
if asset.condition_updates.empty?
-
7
Rails.logger.debug "No condition updates."
-
7
x2 = years_policy
-
7
y2 = condition_threshold
-
7
Rails.logger.debug "x1=#{x1} y1=#{y1} x2=#{x2} y2=#{y2}"
-
7
condition_slope = slope(x1, y1, x2, y2)
-
7
Rails.logger.debug "Slope = #{condition_slope}."
-
else
-
# We determine the new slope from the last data point reported
-
8
condition_report = asset.condition_updates.last
-
-
#Rails.logger.debug condition_report.inspect
-
8
last_rating = condition_report.assessed_rating
-
8
age_at_report = asset.age(condition_report.event_date)
-
-
# Determine the current slope
-
8
x2 = age_at_report
-
8
y2 = last_rating
-
8
Rails.logger.debug "x1=#{x1} y1=#{y1} x2=#{x2} y2=#{y2}"
-
8
condition_slope = slope(x1, y1, x2, y2)
-
8
Rails.logger.debug "Slope = #{condition_slope}."
-
-
# See if we can do a mileage calculation
-
8
unless max_service_life_miles.nil?
-
if max_service_life_miles && age_at_report > 0
-
# Here the slope is based on the number of miles remaining. This keeps the
-
# slope in the same direction
-
y1 = max_service_life_miles
-
y2 = max_service_life_miles - (asset.reported_mileage.nil? ? 0 : asset.reported_mileage)
-
# get the slope
-
mileage_slope = slope(x1, y1, x2, y2)
-
end
-
end
-
end
-
-
15
return {:condition_slope => condition_slope, :mileage_slope => mileage_slope}
-
-
end
-
-
end
-
1
class WeightedAverageConditionRollupCalculator < Calculator
-
-
# Calculates the last year for service given the asset sub type and the date the asset was placed
-
# into service
-
1
def calculate(asset)
-
-
2
Rails.logger.debug "WeightedAverageConditionRollupCalculator.calculate(asset)"
-
-
# only assets with a replacement cost really have a weight so can ignore any without
-
2
return nil if not (asset.respond_to? :reported_condition_rating)
-
5
ratings = asset.dependents.where('scheduled_replacement_cost > 0').map{|x| [x.scheduled_replacement_cost, x.reported_condition_rating]}
-
2
return nil if ratings.size == 0
-
-
1
temp_sum = 0
-
1
ratings.each do |rc|
-
3
temp_sum += rc[0] * (rc[1] || 0) # if no reported condition, default to 0 / Unknown
-
end
-
-
4
return (temp_sum/(ratings.sum{|rc| rc[0]})).round # for now round
-
-
end
-
-
end
-
#-------------------------------------------------------------------------------
-
# ActivitiesController
-
#
-
#
-
#-------------------------------------------------------------------------------
-
1
class ActivitiesController < OrganizationAwareController
-
-
1
before_action :set_activity, :only => [:show, :edit, :update, :destroy]
-
1
before_action :reformat_date_fields, :only => [:create, :update]
-
-
1
INDEX_KEY_LIST_VAR = "activities_list_cache_var"
-
1
ACTIVE_FLAG = '1'
-
1
DASHBOARD_FLAG = '2'
-
1
SYSTEM_FLAG = '3'
-
-
1
ACTIVITY_FLAGS = [
-
["Active", ACTIVE_FLAG],
-
["Show in Dashboard", DASHBOARD_FLAG],
-
["System", SYSTEM_FLAG]
-
]
-
-
1
add_breadcrumb "Home", :root_path
-
1
add_breadcrumb "Activities", :activities_path
-
-
# Lock down the controller
-
1
authorize_resource
-
-
#-----------------------------------------------------------------------------
-
# GET /activities
-
# GET /activities.json
-
#-----------------------------------------------------------------------------
-
1
def index
-
-
# Start to set up the query
-
conditions = []
-
values = []
-
-
@activity_flag_filter = params[:activity_flag_filter]
-
if @activity_flag_filter.blank?
-
@activity_flag_filter = []
-
else
-
if @activity_flag_filter.include? ACTIVE_FLAG
-
conditions << 'active = ?'
-
values << true
-
end
-
if @activity_flag_filter.include? DASHBOARD_FLAG
-
conditions << 'show_in_dashboard = ?'
-
values << true
-
end
-
if @activity_flag_filter.include? SYSTEM_FLAG
-
conditions << 'system_activity = ?'
-
values << true
-
end
-
end
-
-
# Get the activities
-
@activities = Activity.where(conditions.join(' AND '), *values).order("name, created_at DESC")
-
-
# cache the set of object keys in case we need them later
-
cache_list(@activities, INDEX_KEY_LIST_VAR)
-
-
respond_to do |format|
-
format.js
-
format.html
-
format.json { render :json => @activities }
-
end
-
-
end
-
-
#-----------------------------------------------------------------------------
-
# GET /activities/1
-
# GET /activities/1.json
-
#-----------------------------------------------------------------------------
-
1
def show
-
-
add_breadcrumb @activity
-
-
# get the @prev_record_path and @next_record_path view vars
-
get_next_and_prev_object_keys(@activity, INDEX_KEY_LIST_VAR)
-
@prev_record_path = @prev_record_key.nil? ? "#" : activity_path(@prev_record_key)
-
@next_record_path = @next_record_key.nil? ? "#" : activity_path(@next_record_key)
-
-
end
-
-
#-----------------------------------------------------------------------------
-
# GET /activities/new
-
#-----------------------------------------------------------------------------
-
1
def new
-
-
add_breadcrumb "New Activity"
-
-
@activity = Activity.new
-
-
end
-
-
#-----------------------------------------------------------------------------
-
# GET /activities/1/edit
-
#-----------------------------------------------------------------------------
-
1
def edit
-
-
add_breadcrumb @activity, activity_path(@activity)
-
add_breadcrumb "Update"
-
-
end
-
-
#-----------------------------------------------------------------------------
-
# POST /activities
-
# POST /activities.json
-
#-----------------------------------------------------------------------------
-
1
def create
-
-
add_breadcrumb "New Activity"
-
-
@activity = Activity.new(activity_params)
-
-
respond_to do |format|
-
if @activity.save
-
notify_user :notice, 'Activity was successfully created.'
-
format.html { redirect_to @activity }
-
format.json { render :show, status: :created, location: @activity }
-
else
-
format.html { render :new }
-
format.json { render json: @activity.errors, status: :unprocessable_entity }
-
end
-
end
-
end
-
-
#-----------------------------------------------------------------------------
-
# PATCH/PUT /activities/1
-
# PATCH/PUT /activities/1.json
-
#-----------------------------------------------------------------------------
-
1
def update
-
-
add_breadcrumb @activity, activity_path(@activity)
-
add_breadcrumb "Update"
-
-
respond_to do |format|
-
if @activity.update(activity_params)
-
notify_user :notice, 'Activity was successfully updated.'
-
format.html { redirect_to @activity }
-
format.json { render :show, status: :ok, location: @activity }
-
else
-
format.html { render :edit }
-
format.json { render json: @activity.errors, status: :unprocessable_entity }
-
end
-
end
-
end
-
-
#-----------------------------------------------------------------------------
-
# DELETE /activities/1
-
# DELETE /activities/1.json
-
#-----------------------------------------------------------------------------
-
1
def destroy
-
@activity.destroy
-
respond_to do |format|
-
notify_user :notice, 'Activity was successfully removed.'
-
format.html { redirect_to activities_url }
-
format.json { head :no_content }
-
end
-
end
-
-
#-----------------------------------------------------------------------------
-
1
private
-
#-----------------------------------------------------------------------------
-
-
# Use callbacks to share common setup or constraints between actions.
-
1
def set_activity
-
@activity = Activity.find_by(:object_key => params[:id])
-
if @activity.nil?
-
redirect_to '/404'
-
return
-
end
-
end
-
-
# Never trust parameters from the scary internet, only allow the white list through.
-
1
def activity_params
-
params.require(:activity).permit(Activity.allowable_params)
-
end
-
-
1
def reformat_date(date_str)
-
form_date = Date.strptime(date_str, '%m/%d/%Y')
-
return form_date.strftime('%Y-%m-%d')
-
end
-
-
1
def reformat_date_fields
-
params[:activity][:start_date] = reformat_date(params[:activity][:start_date]) unless params[:activity][:start_date].blank?
-
params[:activity][:end_date] = reformat_date(params[:activity][:end_date]) unless params[:activity][:end_date].blank?
-
end
-
-
end
-
1
class ActivityLogsController < OrganizationAwareController
-
-
1
add_breadcrumb "Home", :root_path
-
1
before_action :set_activity, only: [:show]
-
-
1
INDEX_KEY_LIST_VAR = "activity_log_key_list_cache_var"
-
-
# Protect controller methods using the cancan ability
-
1
authorize_resource
-
-
1
def index
-
-
1
add_breadcrumb "Activity Log"
-
-
# get the policies for this agency
-
1
@activities = ActivityLog.where(organization: @organization_list).order('activity_time')
-
-
# cache the set of object keys in case we need them later
-
#cache_list(@activities, INDEX_KEY_LIST_VAR)
-
-
1
respond_to do |format|
-
1
format.html # index.html.erb
-
1
format.json { render :json => @activities }
-
end
-
end
-
-
1
def show
-
1
add_breadcrumb "Activity Log", activity_logs_path
-
1
add_breadcrumb @activity
-
-
# get the @prev_record_path and @next_record_path view vars
-
#get_next_and_prev_object_keys(@activity, INDEX_KEY_LIST_VAR)
-
#@prev_record_path = @prev_record_key.nil? ? "#" : activity_log_path(@prev_record_key)
-
#@next_record_path = @next_record_key.nil? ? "#" : activity_log_path(@next_record_key)
-
-
1
respond_to do |format|
-
1
format.html # index.html.erb
-
1
format.json { render :json => @activity }
-
end
-
-
end
-
-
1
private
-
# Use callbacks to share common setup or constraints between actions.
-
1
def set_activity
-
1
@activity = ActivityLog.find(params[:id]) unless params[:id].nil?
-
1
if @activity.nil?
-
redirect_to '/404'
-
return
-
end
-
end
-
-
end
-
1
module Api
-
## Base controller for API controllers
-
1
class ApiController < ActionController::API
-
1
include ActionView::Layouts
-
1
layout "jsend"
-
-
1
skip_before_action :authenticate_user!, :verify_authenticity_token, raise: false
-
1
acts_as_token_authentication_handler_for User, fallback: :none
-
-
1
before_action :require_authentication
-
-
1
respond_to :json
-
-
# Catches 500 errors and sends back JSON with headers.
-
1
include JsonResponseHelper::ApiErrorCatcher
-
-
1
before_action :initialize_errors_hash
-
-
1
def touch_session
-
1
render status: 200,
-
json: {}
-
end
-
-
1
protected
-
-
# Actions to take after successfully authenticated a user token.
-
# This is run automatically on successful token authentication
-
1
def after_successful_token_authentication
-
30
@user = current_user
-
end
-
-
# Initializes an empty errors hash, before each action
-
1
def initialize_errors_hash
-
32
@errors = {}
-
end
-
-
# Returns a hash of authentication headers
-
1
def auth_headers
-
{
-
email: request.headers["X-User-Email"],
-
authentication_token: request.headers["X-User-Token"]
-
}
-
end
-
-
# Returns true if authentication has successfully completed
-
1
def authentication_successful?
-
33
@user.present?
-
end
-
-
# Renders a 401 failure response if authentication was not successful
-
1
def require_authentication
-
33
render_failed_auth_response unless authentication_successful? # render a 401 error
-
end
-
-
# Renders a failed user auth response
-
1
def render_failed_auth_response
-
3
render status: 401,
-
json: json_response(:fail, data: {user: "Valid email and token must be present."})
-
end
-
-
end
-
end
-
1
class Api::V1::AssetsController < Api::ApiController
-
# Given asset object key, look up asset profile
-
# GET /assets/{id}
-
1
def show
-
@asset = get_selected_asset(params[:id])
-
unless @asset
-
@status = :fail
-
@data = {id: "Asset #{params[:id]} not found."}
-
render status: :not_found, json: json_response(:fail, data: @data)
-
end
-
end
-
-
1
def index
-
total_assets = get_assets
-
@assets = paginate total_assets.page(params[:page]).per(params[:page_size])
-
end
-
-
1
private
-
-
1
def get_selected_asset(asset_id, convert=true)
-
# TODO: add access control (e.g., viewable_organizations)
-
-
selected_asset = base_asset_class.find_by(:object_key => asset_id) unless asset_id.blank?
-
-
if convert
-
asset = Rails.application.config.asset_base_class_name.constantize.get_typed_asset(selected_asset)
-
else
-
asset = selected_asset
-
end
-
-
asset
-
end
-
-
1
def get_assets
-
# TODO: filtering
-
base_asset_class.all
-
end
-
-
1
def base_asset_class
-
Rails.application.config.asset_base_class_name.constantize
-
end
-
end
-
1
class Api::V1::DocumentsController < Api::V1::NestedResourceController
-
1
before_action :set_documentable, :only => [:index, :create]
-
1
before_action :set_document, :only => [:show, :update, :destroy]
-
-
# GET /documents.json
-
1
def index
-
1
@documents = @documentable.documents
-
end
-
-
# GET /documents/1.json
-
1
def show
-
end
-
-
# POST /documents.json
-
1
def create
-
4
@document = @documentable.documents.build(form_params)
-
4
@document.creator = current_user
-
4
unless @document.save
-
1
@status = :fail
-
1
@message = "Unable to upload document due the following error: #{@document.errors.messages}"
-
1
render status: 400, json: json_response(:fail, message: @message)
-
end
-
end
-
-
# PATCH/PUT /documents/1.json
-
1
def update
-
1
unless @document.update(form_params)
-
@status = :fail
-
@message = "Unable to update document due the following error: #{@document.errors.messages}"
-
render status: 400, json: json_response(:fail, message: @message)
-
end
-
end
-
-
# DELETE /documents/1.json
-
1
def destroy
-
1
unless @document.destroy
-
@status = :fail
-
@message = "Unable to destroy document due the following error: #{@document.errors.messages}"
-
render status: 400, json: json_response(:fail, message: @message)
-
end
-
end
-
-
1
private
-
-
1
def set_documentable
-
6
@documentable = find_resource
-
6
unless @documentable
-
1
@status = :fail
-
1
@data = {id: "Documentable object not found."}
-
1
render status: :not_found, json: json_response(:fail, data: @data)
-
end
-
end
-
-
# Use callbacks to share common setup or constraints between actions.
-
1
def set_document
-
2
@document = Document.find_by(:object_key => params[:id])
-
-
2
unless @document
-
@status = :fail
-
@data = {id: "Document #{params[:id]} not found."}
-
render status: :not_found, json: json_response(:fail, data: @data)
-
end
-
end
-
-
# Never trust parameters from the scary internet, only allow the white list through.
-
1
def form_params
-
5
params.permit(Document.allowable_params)
-
end
-
-
end
-
1
class Api::V1::ImagesController < Api::V1::NestedResourceController
-
1
before_action :set_imagable, :only => [:index, :create]
-
1
before_action :set_image, :only => [:show, :update, :destroy]
-
-
# GET /images.json
-
1
def index
-
1
@images = @imagable.images
-
end
-
-
# GET /images/1.json
-
1
def show
-
end
-
-
# POST /images.json
-
1
def create
-
# If image param is string, assume base64 encoding of the image data.
-
# Decode and store back in image parameter
-
4
if params[:image].is_a? String
-
image_data = params[:image]
-
io = CarrierStringIO.new(Base64.decode64(image_data))
-
io.original_filename = params[:original_filename]
-
io.content_type = params[:content_type]
-
params[:image] = io
-
end
-
4
@image = @imagable.images.build(form_params)
-
4
@image.base_imagable = @image.imagable if @image.base_imagable.nil?
-
4
@image.creator = current_user
-
4
unless @image.save
-
1
@status = :fail
-
1
@message = "Unable to upload image due the following error: #{@image.errors.messages}"
-
1
render status: 400, json: json_response(:fail, message: @message)
-
end
-
end
-
-
# PATCH/PUT /images/1.json
-
1
def update
-
1
unless @image.update(form_params)
-
@status = :fail
-
@message = "Unable to update image due the following error: #{@image.errors.messages}"
-
render status: 400, json: json_response(:fail, message: @message)
-
end
-
end
-
-
# DELETE /images/1.json
-
1
def destroy
-
1
unless @image.destroy
-
@status = :fail
-
@message = "Unable to destroy image due the following error: #{@image.errors.messages}"
-
render status: 400, json: json_response(:fail, message: @message)
-
end
-
end
-
-
1
protected
-
-
# Never trust parameters from the scary internet, only allow the white list through.
-
1
def form_params
-
5
params.permit(Image.allowable_params)
-
end
-
-
1
private
-
-
1
def set_imagable
-
6
@imagable = find_resource
-
6
unless @imagable
-
1
@status = :fail
-
1
@data = {id: "Imagable object not found."}
-
1
render status: :not_found, json: json_response(:fail, data: @data)
-
end
-
end
-
-
# Use callbacks to share common setup or constraints between actions.
-
1
def set_image
-
2
@image = Image.find_by(:object_key => params[:id])
-
-
2
unless @image
-
@status = :fail
-
@data = {id: "Image #{params[:id]} not found."}
-
render status: :not_found, json: json_response(:fail, data: @data)
-
end
-
end
-
-
end
-
#
-
# Use this class as the base class for API controllers which deal with polymorphic classes
-
# such as comments (commentable), documents (documetable), etc.
-
#
-
1
class Api::V1::NestedResourceController < Api::ApiController
-
-
1
protected
-
-
# Get the class and object key of the commentable object we are operating on. There is a special
-
# case where the controller is aliased and we need to determine this and replace the correct
-
# resource class name
-
1
def find_resource
-
12
params.permit!.to_h.reverse_each do |name, value|
-
48
if name =~ /(.+)_id$/
-
12
if $1 == 'asset'
-
12
return Rails.application.config.asset_base_class_name.constantize.find_by(object_key: value)
-
else
-
return $1.classify.constantize.find_by(object_key: value)
-
end
-
end
-
end
-
end
-
-
end
-
1
class Api::V1::OrganizationsController < Api::ApiController
-
# Given organization_id, look up org profile
-
# GET /organizations/{id}
-
1
def show
-
4
get_organization(params[:id])
-
-
4
unless @organization
-
2
@status = :fail
-
2
@data = {id: "Organization #{params[:id]} not found."}
-
2
render status: :not_found, json: json_response(:fail, data: @data)
-
end
-
end
-
-
1
private
-
-
1
def get_organization(org_id)
-
4
org_id = org_id.to_i unless org_id.blank?
-
-
4
if @user.viewable_organization_ids.include?(org_id)
-
2
@organization = Organization.find_by_id(org_id)
-
end
-
end
-
end
-
1
class Api::V1::SessionsController < Api::ApiController
-
1
skip_before_action :require_authentication, only: [:create]
-
-
# Signs in an existing user, returning auth token
-
# POST /sign_in
-
# Leverages devise lockable module: https://github.com/plataformatec/devise/blob/master/lib/devise/models/lockable.rb
-
1
def create
-
2
@user = User.find_by(email: params[:email].downcase)
-
2
@fail_status = :bad_request
-
-
# Check if a user was found based on the passed email. If so, continue authentication.
-
2
if @user.present?
-
# checks if password is incorrect and user is locked, and unlocks if lock is expired
-
2
if @user.valid_for_api_authentication?(params[:password])
-
1
@user.ensure_authentication_token
-
else
-
# Otherwise, add some errors to the response depending on what went wrong.
-
-
# Wokaround for useful but protected method
-
1
if @user.on_last_attempt?
-
@errors[:last_attempt] = "You have one more attempt before account is locked for #{User.unlock_in / 60} minutes."
-
end
-
-
1
if @user.access_locked?
-
@errors[:locked] = "User account is temporarily locked. Try again in #{@user.time_until_unlock} minutes."
-
end
-
-
1
unless @user.access_locked? || @user.valid_password?(params[:password])
-
1
@errors[:password] = "Incorrect password for #{@user.email}."
-
end
-
-
1
@fail_status = :unauthorized
-
1
@errors = @errors.merge(@user.errors.to_h)
-
end
-
else
-
@errors[:email] = "Could not find user with email #{params[:email]}"
-
end
-
-
# Check if any errors were recorded. If not, send a success response.
-
2
if @errors.empty?
-
1
@message = "User Signed In Successfully"
-
else # If there are any errors, send back a failure response.
-
1
@status = :fail
-
1
@data = {errors: @errors}
-
1
render status: @fail_status, json: json_response(:fail, data: @data)
-
end
-
end
-
-
# Signs out a user based on email and auth token headers
-
# DELETE /sign_out
-
1
def destroy
-
-
1
if @user && @user.reset_authentication_token
-
1
@message = "User #{@user.email} successfully signed out."
-
else
-
@status = :fail
-
@data = {
-
message: "Could not sign out user",
-
auth_headers: auth_headers
-
}
-
render status: :bad_request, json: json_response(:fail, message: data)
-
end
-
end
-
end
-
1
class Api::V1::UsersController < Api::ApiController
-
# Given email, look up user profile
-
# GET /users/profile
-
1
def profile
-
4
@user = User.find_by(email: params[:email].try(:downcase))
-
4
unless @user
-
2
@status = :fail
-
2
@data = {email: "User #{params[:email]} not found."}
-
2
render status: :not_found, json: json_response(:fail, data: @data)
-
end
-
end
-
-
# Given organization_id, look up users
-
# GET /users
-
1
def index
-
4
@organization = Organization.find_by_id(params[:organization_id])
-
4
if @organization
-
2
@users = @organization.users
-
else
-
2
@status = :fail
-
2
@data = {organization: "Organization #{params[:organization_id]} not found."}
-
2
render status: :not_found, json: json_response(:fail, data: @data)
-
end
-
end
-
end
-
#
-
# Abstract controller that is used as the base class
-
# for any concrete controllers that are based on an asset
-
#
-
1
class AssetAwareController < OrganizationAwareController
-
# set the @asset variable before any actions are invoked
-
1
before_action :get_asset
-
-
# From the application config
-
1
MAX_RETURNS_FOR_SEARCH = Rails.application.config.ui_search_items
-
-
# default is to always generate typed assets (eg vehicle, railcar etc) rather
-
# than untyped (asset) assets
-
1
RENDER_TYPED_ASSETS = true
-
-
# returns details for an asset subtype using an ajax query
-
# NOT USED
-
1
def details
-
@asset_subtype = AssetSubtype.find(params[:asset_subtype])
-
-
respond_to do |format|
-
format.js
-
format.json { render :json => @asset_subtype.to_json }
-
end
-
-
end
-
-
1
protected
-
-
1
def render_typed_assets
-
RENDER_TYPED_ASSETS
-
end
-
-
# returns the asset that has been selected by the user. The asset musst
-
# belong to the users' selected agency. The query returns nil if the asset
-
# is not found in the agencies asset list.
-
#
-
# if convert == true a typed version of the asset is returned
-
# otherwise a base class asset is returned
-
#
-
1
def get_selected_asset(convert=true)
-
11
selected_asset = Rails.application.config.asset_base_class_name.constantize.find_by(:organization_id => @organization_list, :object_key => params[:inventory_id]) unless params[:inventory_id].blank?
-
11
if convert
-
11
asset = Rails.application.config.asset_base_class_name.constantize.get_typed_asset(selected_asset)
-
else
-
asset = selected_asset
-
end
-
11
return asset
-
end
-
-
1
def get_asset
-
# check that the asset is owned by the agency
-
11
@asset = get_selected_asset(render_typed_assets)
-
11
if @asset.nil?
-
if TransamAsset.find_by(:organization_id => current_user.user_organization_filters.system_filters.first.get_organizations.map{|x| x.id}, :object_key => params[:id]).nil?
-
redirect_to '/404'
-
else
-
notify_user(:warning, 'This record is outside your filter. Change your filter if you want to access it.')
-
redirect_to inventory_index_path
-
end
-
return
-
end
-
end
-
-
end
-
1
class AssetEventsController < AssetAwareController
-
-
1
add_breadcrumb "Home", :root_path
-
-
# set the @asset_event variable before any actions are invoked
-
1
before_action :get_asset_event, :only => [:show, :edit, :update, :destroy, :fire_workflow_event, :popup]
-
1
before_action :check_for_cancel, :only => [:create, :update]
-
1
before_action :reformat_date_field, :only => [:create, :update]
-
-
1
skip_before_action :get_asset, :only => [:get_summary, :popup]
-
-
# always use generic untyped assets for this controller
-
1
RENDER_TYPED_ASSETS = true
-
-
# Lock down the controller
-
1
authorize_resource only: [:index, :show, :new, :create, :edit, :update, :destroy]
-
-
# always render untyped assets for this controller
-
1
def render_typed_assets
-
11
RENDER_TYPED_ASSETS
-
end
-
-
1
def get_summary
-
asset_event_type = AssetEventType.find_by(id: params[:asset_event_type_id])
-
-
unless asset_event_type.nil?
-
asset_event_klass = asset_event_type.class_name.constantize
-
-
if params[:order].blank?
-
results = asset_event_klass.all
-
else
-
results = asset_event_klass.unscoped.order(params[:order])
-
end
-
-
if asset_event_klass.count > 0
-
asset_klass = asset_event_klass.first.send(Rails.application.config.asset_base_class_name.underscore).class
-
-
asset_joins = [Rails.application.config.asset_base_class_name.underscore]
-
-
while asset_klass.try(:acting_as_name)
-
asset_joins << asset_klass.acting_as_name
-
asset_klass = asset_klass.acting_as_name.classify.constantize
-
end
-
-
-
idx = asset_joins.length-2
-
join_relations = Hash.new
-
join_relations[asset_joins[idx]] = asset_joins[idx+1]
-
idx -= 1
-
while idx >= 0
-
tmp = Hash.new
-
tmp[asset_joins[idx]] = join_relations
-
join_relations = tmp
-
idx -= 1
-
end
-
-
results = results.includes(join_relations).where(transam_asset: asset_event_klass.first.send(Rails.application.config.asset_base_class_name.underscore).class.where(organization_id: @organization_list))
-
end
-
-
unless params[:scope].blank?
-
if asset_event_klass.respond_to? params[:scope]
-
results = results.send(params[:scope])
-
end
-
end
-
-
-
-
-
respond_to do |format|
-
format.js {
-
render partial: "dashboards/#{asset_event_type.class_name.underscore}_widget_table", locals: {results: results }
-
}
-
end
-
-
end
-
end
-
-
1
def popup
-
-
end
-
-
1
def index
-
-
2
add_asset_breadcrumbs
-
2
add_breadcrumb "History"
-
-
# Check to see if we got a filter to sub select on
-
2
if params[:filter_type]
-
# see if it was blank
-
1
if params[:filter_type].blank?
-
@filter_type = 0
-
else
-
1
@filter_type = params[:filter_type].to_i
-
end
-
else
-
# See if there is one in the session
-
1
@filter_type = session[:filter_type].nil? ? 0 : session[:filter_type]
-
end
-
# store it in the session
-
2
session[:filter_type] = @filter_type
-
-
2
if @filter_type == 0
-
1
@events = @asset.asset_events
-
else
-
1
@events = @asset.asset_events.where('asset_event_type_id = ?', @filter_type)
-
end
-
-
2
@page_title = "#{@asset.name}: History"
-
-
2
respond_to do |format|
-
2
format.html # index.html.erb
-
2
format.json { render :json => @events }
-
end
-
-
end
-
-
1
def new
-
-
# get the asset event type
-
1
asset_event_type = AssetEventType.find(params[:event_type])
-
1
unless asset_event_type.blank?
-
1
@asset_event = @asset.build_typed_event(asset_event_type.class_name.constantize)
-
end
-
-
1
if params[:transferred] == '1'
-
@transferred = true
-
end
-
-
1
unless params[:causal_asset_event_id].nil?
-
@causal_asset_event_id = params[:causal_asset_event_id]
-
end
-
-
1
unless params[:causal_asset_event_name].nil?
-
@causal_asset_event_name = params[:causal_asset_event_name]
-
end
-
-
1
add_new_show_create_breadcrumbs
-
-
1
respond_to do |format|
-
1
@ajax_request = ajax_request?
-
1
format.html
-
1
format.js
-
1
format.json { render :json => @asset_event }
-
end
-
-
end
-
-
1
def show
-
-
# if not found or the object does not belong to the asset
-
# send them back to index.html.erb
-
1
if @asset_event.nil?
-
notify_user(:alert, 'Record not found!')
-
redirect_to(inventory_url(@asset))
-
return
-
end
-
-
1
add_new_show_create_breadcrumbs
-
-
1
respond_to do |format|
-
1
format.html # show.html.erb
-
1
format.json { render :json => @asset_event }
-
end
-
end
-
-
1
def edit
-
-
# if not found or the object does not belong to the asset
-
# send them back to index.html.erb
-
1
if @asset_event.nil?
-
notify_user(:alert, 'Record not found!')
-
redirect_to(inventory_url(@asset))
-
return
-
end
-
-
1
add_edit_update_breadcrumbs
-
-
1
respond_to do |format|
-
1
@ajax_request = ajax_request?
-
1
format.html
-
1
format.js
-
1
format.json { render :json => @asset_event }
-
end
-
-
end
-
-
1
def update
-
-
# get variables for updating view via JS if form sent remotely
-
1
@ajax_request = ajax_request?
-
1
if @ajax_request
-
@view_div = params[:view_div]
-
@view_name = params[:view_name]
-
end
-
-
# if not found or the object does not belong to the asset
-
# send them back to index.html.erb
-
1
if @asset_event.nil?
-
notify_user(:alert, 'Record not found!')
-
redirect_to(inventory_url(@asset))
-
return
-
end
-
-
1
@asset_event.updater = current_user
-
-
1
add_edit_update_breadcrumbs
-
-
1
respond_to do |format|
-
1
if @asset_event.update_attributes(form_params)
-
-
1
notify_user(:notice, "Event was successfully updated.")
-
-
# The event was updated so we need to update the asset.
-
#fire_asset_update_event(@asset_event.asset_event_type, @asset)
-
-
2
format.html { redirect_to inventory_url(@asset) }
-
1
format.js
-
1
format.json { head :no_content }
-
else
-
format.html { render "edit" }
-
format.js { render "edit" }
-
format.json { render :json => @asset_event.errors, :status => :unprocessable_entity }
-
end
-
end
-
end
-
-
1
def create
-
-
# get variables for updating view via JS if form sent remotely
-
-
1
@ajax_request = ajax_request?
-
1
if @ajax_request
-
@view_div = params[:view_div]
-
@view_name = params[:view_name]
-
end
-
-
# we need to know what the event type was for this event
-
1
asset_event_type = AssetEventType.find(params[:event_type])
-
1
unless asset_event_type.blank?
-
1
assoc_name = asset_event_type.class_name.gsub('Event', '').underscore.pluralize
-
1
assoc_name = 'early_disposition_requests' if assoc_name == 'early_disposition_request_updates'
-
1
owner = @asset.send(assoc_name).proxy_association.owner
-
1
asset_params = Hash.new
-
1
asset_params[Rails.application.config.asset_base_class_name.underscore] = owner
-
1
@asset_event = asset_event_type.class_name.constantize.new(form_params.merge(asset_params))
-
1
@asset_event.creator = current_user
-
end
-
-
1
unless params[:causal_asset_event_id].nil?
-
@causal_asset_event = AssetEvent.find_by(:object_key => params[:causal_asset_event_id])
-
end
-
-
1
unless params[:causal_asset_event_name].nil?
-
@causal_asset_event_name = params[:causal_asset_event_name]
-
end
-
-
1
add_new_show_create_breadcrumbs
-
-
1
respond_to do |format|
-
1
if @asset_event.save
-
1
Rails.logger.debug @asset_event.inspect
-
-
1
notify_user(:notice, "Event was successfully created.")
-
-
# The event was removed so we need to update the asset
-
#fire_asset_update_event(@asset_event.asset_event_type, @asset)
-
-
# if notification enabled, then send out
-
1
if @asset_event.class.try(:workflow_notification_enabled?)
-
@asset_event.notify_event_by(current_user, :new)
-
end
-
-
#If another event resulted in this event we should provess the other event as well
-
1
unless @causal_asset_event.nil? || @causal_asset_event_name.nil?
-
@causal_asset_event = AssetEvent.as_typed_event @causal_asset_event
-
if @causal_asset_event.class.name == 'EarlyDispositionRequestUpdateEvent' && @causal_asset_event_name == 'approve_via_transfer'
-
@causal_asset_event.state = 'transfer_approved'
-
@causal_asset_event.save
-
end
-
end
-
-
2
format.html { redirect_to inventory_url(@asset) }
-
1
format.js
-
1
format.json { render :json => @asset_event, :status => :created, :location => @asset_event }
-
else
-
Rails.logger.debug @asset_event.errors.inspect
-
format.html { render :action => "new" }
-
format.js { render :action => "new" }
-
format.json { render :json => @asset_event.errors, :status => :unprocessable_entity }
-
end
-
end
-
end
-
-
1
def destroy
-
-
# if not found or the object does not belong to the asset
-
# send them back to index.html.erb
-
1
if @asset_event.nil?
-
notify_user(:alert, 'Record not found!')
-
redirect_to(inventory_url(@asset))
-
return
-
end
-
-
1
asset_event_type = @asset_event.asset_event_type
-
1
@asset_event.destroy
-
-
1
notify_user(:notice, "Event was successfully removed.")
-
-
# The event was removed so we need to update the asset condition
-
#fire_asset_update_event(asset_event_type, @asset)
-
-
1
respond_to do |format|
-
2
format.html { redirect_to(inventory_url(@asset)) }
-
1
format.json { head :no_content }
-
end
-
end
-
-
1
def fire_workflow_event
-
-
# Check that this is a valid event name for the state machines
-
3
asset_event_class = @asset_event.class
-
-
3
if asset_event_class.try(:event_names) && asset_event_class.event_names.include?(params[:event])
-
3
event_name = params[:event]
-
-
# special cases
-
# jump to final disposition page if a manager approves an early disposition request via transfer
-
3
if asset_event_class.name == 'EarlyDispositionRequestUpdateEvent' && event_name == "approve_via_transfer"
-
1
is_redirected = true
-
# we do not want to fire approval of the application event approval
-
1
redirect_to new_inventory_asset_event_path(@asset_event.send(Rails.application.config.asset_base_class_name.underscore), :event_type => DispositionUpdateEvent.asset_event_type.id, :transferred => 1, :causal_asset_event_id => @asset_event.object_key, :causal_asset_event_name => event_name)
-
2
elsif @asset_event.fire_state_event(event_name)
-
2
event = WorkflowEvent.new
-
2
event.creator = current_user
-
2
event.accountable = @asset_event
-
2
event.event_type = event_name
-
2
event.save
-
-
# if notification enabled, then send out
-
2
if asset_event_class.try(:workflow_notification_enabled?)
-
2
@asset_event.notify_event_by(current_user, event_name)
-
end
-
-
else
-
notify_user(:alert, "Could not #{event_name.humanize} asset event #{@asset_event}")
-
end
-
-
else
-
notify_user(:alert, "#{params[:event_name]} is not a valid event for a #{asset_event_class.name}")
-
end
-
-
3
unless is_redirected
-
2
redirect_back fallback_location: root_path
-
end
-
-
end
-
-
#------------------------------------------------------------------------------
-
#
-
# Protected Methods
-
#
-
#------------------------------------------------------------------------------
-
1
protected
-
-
# Updates the asset by running the appropriate job. The job is based on the
-
# type of event that was modified. If the job requires the asset SOGR metrics be updated
-
# then the SOGR update job is queued for the asset
-
1
def fire_asset_update_event(asset_event_type, asset, priority = 0)
-
if asset_event_type && asset && !asset_event_type.job_name.blank?
-
klass = asset_event_type.job_name.constantize
-
job = klass.new(asset.object_key)
-
begin
-
# Run the job
-
job.perform
-
# See if the job also needs to update the assets SOGR
-
if job.requires_sogr_update?
-
next_job = AssetSogrUpdateJob.new(asset.object_key)
-
fire_background_job(next_job, priority)
-
end
-
rescue Exception => e
-
Rails.logger.warn e.message
-
end
-
end
-
end
-
-
-
1
def get_asset_event
-
7
asset_event = AssetEvent.find_by_object_key(params[:id]) unless params[:id].nil?
-
7
if asset_event
-
7
@asset_event = AssetEvent.as_typed_event(asset_event)
-
else
-
redirect_to '/404'
-
end
-
end
-
-
1
def add_asset_breadcrumbs
-
7
add_breadcrumb @asset.asset_type.name.pluralize(2), inventory_index_path(:asset_type => @asset.asset_type, :asset_subtype => 0)
-
7
add_breadcrumb @asset.asset_subtype.name.pluralize(2), inventory_index_path(:asset_subtype => @asset.asset_subtype)
-
7
add_breadcrumb @asset.asset_tag, inventory_path(@asset)
-
end
-
-
1
def add_new_show_create_breadcrumbs
-
3
add_asset_breadcrumbs
-
3
add_breadcrumb "#{@asset_event.asset_event_type.name} Update"
-
end
-
-
1
def add_edit_update_breadcrumbs
-
2
add_asset_breadcrumbs
-
2
add_breadcrumb @asset_event.asset_event_type.name, edit_inventory_asset_event_path(@asset, @asset_event)
-
2
add_breadcrumb "Update"
-
end
-
-
-
#------------------------------------------------------------------------------
-
#
-
# Private Methods
-
#
-
#------------------------------------------------------------------------------
-
1
private
-
-
1
def reformat_date_field
-
2
date_str = params[:asset_event][:event_date]
-
2
if date_str.present?
-
2
form_date = Date.strptime(date_str, '%m/%d/%Y')
-
2
params[:asset_event][:event_date] = form_date.strftime('%Y-%m-%d')
-
end
-
end
-
-
# Never trust parameters from the scary internet, only allow the white list through.
-
1
def form_params
-
2
params.require(:asset_event).permit(asset_event_allowable_params)
-
end
-
-
1
def check_for_cancel
-
# go back to the asset view
-
2
unless params[:cancel].blank?
-
redirect_to(inventory_url(@asset))
-
end
-
end
-
-
end
-
1
class AssetGroupsController < OrganizationAwareController
-
-
1
before_action :set_asset_group, :only => [:show, :edit, :update, :destroy]
-
-
1
add_breadcrumb "Home", :root_path
-
-
# Lock down the controller
-
1
authorize_resource
-
-
# GET /asset_groups
-
1
def index
-
-
1
add_breadcrumb 'Asset Groups', asset_groups_path
-
-
1
@asset_groups = @organization.asset_groups
-
-
end
-
-
# GET /asset_groups/1
-
1
def show
-
-
1
add_breadcrumb 'Asset Groups', asset_groups_path
-
1
add_breadcrumb @asset_group
-
-
1
rep = AssetSubtypeReport.new
-
1
@data = rep.get_data_from_collection(@asset_group.assets)
-
1
@total_assets = @asset_group.assets.count
-
-
end
-
-
# GET /asset_groups/new
-
1
def new
-
-
1
add_breadcrumb 'Asset Groups', asset_groups_path
-
1
add_breadcrumb 'New'
-
-
1
@asset_group = AssetGroup.new
-
-
# See if the user added a flag to load from a search results
-
1
if params[:from_search] == "1"
-
@use_cached_assets = "1"
-
end
-
-
end
-
-
# GET /asset_groups/1/edit
-
1
def edit
-
-
1
add_breadcrumb 'Asset Groups', asset_groups_path
-
1
add_breadcrumb @asset_group, asset_group_path(@asset_group)
-
1
add_breadcrumb 'Update'
-
-
end
-
-
# POST /asset_groups
-
1
def create
-
-
1
add_breadcrumb 'Asset Groups', asset_groups_path
-
1
add_breadcrumb 'New'
-
-
1
@asset_group = AssetGroup.new(form_params)
-
1
@asset_group.active = true
-
1
@asset_group.organization = @organization
-
-
1
if @asset_group.save
-
-
# See if the user is creating the group from a set of cached assets
-
1
if params[:use_cached_assets] == "1"
-
# Populate the asset group from the cached results
-
cache_key = AssetsController::INDEX_KEY_LIST_VAR
-
assets = Asset.where(object_key: get_cached_objects(cache_key))
-
assets.each do |asset|
-
a = Asset.get_typed_asset(asset)
-
@asset_group.assets << a
-
end
-
# clear out the cached items once they're in a group
-
clear_cached_objects(cache_key)
-
notify_user(:notice, "Asset group #{@asset_group} was successfully created and #{@asset_group.assets.count} assets were added.")
-
else
-
1
notify_user(:notice, "Asset group #{@asset_group} was successfully created.")
-
end
-
-
1
redirect_to asset_group_path(@asset_group)
-
else
-
render :new
-
end
-
-
end
-
-
# PATCH/PUT /issues/1
-
1
def update
-
-
1
add_breadcrumb 'Asset Groups', asset_groups_path
-
1
add_breadcrumb @asset_group, asset_group_path(@asset_group)
-
1
add_breadcrumb 'Update'
-
-
1
if @asset_group.update(form_params)
-
1
notify_user(:notice, "Asset group #{@asset_group} was successfully updated.")
-
1
redirect_to @asset_group
-
else
-
render :edit
-
end
-
end
-
-
# DELETE /issues/1
-
1
def destroy
-
-
1
name = @asset_group.name
-
1
@asset_group.destroy
-
-
1
notify_user(:notice, "Asset group #{name} was successfully removed.")
-
1
redirect_to asset_groups_url
-
end
-
-
1
private
-
# Use callbacks to share common setup or constraints between actions.
-
1
def set_asset_group
-
4
@asset_group = AssetGroup.find_by_object_key(params[:id]) unless params[:id].nil?
-
4
if @asset_group.nil?
-
redirect_to '/404'
-
return
-
end
-
end
-
-
# Only allow a trusted parameter "white list" through.
-
1
def form_params
-
2
params.require(:asset_group).permit(AssetGroup.allowable_params)
-
end
-
end
-
1
class AssetsController < AssetAwareController
-
-
1
add_breadcrumb "Home", :root_path
-
-
# Set the view variabless form the params @asset_type, @asset_subtype, @search_text, @spatial_filter, @view
-
1
before_action :set_view_vars, :only => [:index, :map]
-
# set the @asset variable before any actions are invoked
-
1
before_action :get_asset, :only => [:tag, :show, :edit, :copy, :update, :destroy, :summary_info, :add_to_group, :remove_from_group, :popup, :get_dependents, :add_dependents, :get_dependent_subform, :get_subheader]
-
1
before_action :reformat_date_fields, :only => [:create, :update]
-
# Update the vendor_id param if the user is using the vendor_name parameter
-
1
before_action :update_vendor_param, :only => [:create, :update]
-
-
# Lock down the controller
-
1
authorize_resource only: [:index, :show, :new, :create, :edit, :update, :destroy]
-
-
1
STRING_TOKENIZER = '|'
-
-
# Session Variables
-
1
INDEX_KEY_LIST_VAR = "asset_key_list_cache_var"
-
-
# Returns a JSON array of matching asset subtypes based on a typeahead name or description
-
1
def filter(klass=Rails.application.config.asset_base_class_name.constantize)
-
-
query = params[:query]
-
query_str = "%" + query + "%"
-
Rails.logger.debug query_str
-
-
matches = []
-
assets = klass.where(organization_id: current_user.viewable_organization_ids)
-
.where(params[:search_params])
-
.where("(asset_tag LIKE ? OR object_key LIKE ? OR description LIKE ?)", query_str, query_str, query_str)
-
-
assets.each do |asset|
-
matches << {
-
"id" => asset.object_key,
-
"name" => "#{asset.to_s}: #{asset.description}"
-
}
-
end
-
-
respond_to do |format|
-
format.js { render :json => matches.to_json }
-
format.json { render :json => matches.to_json }
-
end
-
-
end
-
-
# Adds the asset to the specified group
-
1
def add_to_group
-
asset_group = AssetGroup.find_by_object_key(params[:asset_group])
-
if asset_group.nil?
-
notify_user(:alert, "Can't find the asset group selected.")
-
else
-
if @asset.asset_groups.exists? asset_group.id
-
notify_user(:alert, "Asset #{@asset} is already a member of '#{asset_group}'.")
-
else
-
@asset.asset_groups << asset_group
-
notify_user(:notice, "Asset #{@asset} was added to '#{asset_group}'.")
-
end
-
end
-
-
# Always display the last view
-
redirect_back(fallback_location: root_path)
-
end
-
-
# Removes the asset from the specified group
-
1
def remove_from_group
-
asset_group = AssetGroup.find_by_object_key(params[:asset_group])
-
if asset_group.nil?
-
notify_user(:alert, "Can't find the asset group selected.")
-
else
-
if @asset.asset_groups.exists? asset_group.id
-
@asset.asset_groups.delete asset_group
-
notify_user(:notice, "Asset #{@asset} was removed from '#{asset_group}'.")
-
else
-
notify_user(:alert, "Asset #{@asset} is not a member of '#{asset_group}'.")
-
end
-
end
-
-
# Always display the last view
-
redirect_back(fallback_location: root_path)
-
end
-
-
# NOT USED
-
1
def parent
-
parent_asset = @organization.assets.find_by_object_key(params[:parent_key])
-
respond_to do |format|
-
format.js {render :partial => 'assets/asset_details', :locals => { :asset => parent_asset} }
-
end
-
end
-
-
1
def get_summary
-
asset_type_id = params[:asset_type_id]
-
-
if asset_type_id.blank?
-
results = ActiveRecord::Base.connection.exec_query(Rails.application.config.asset_base_class_name.constantize.operational.select('organization_id, asset_subtypes.asset_type_id, organizations.short_name AS org_short_name, asset_types.name AS subtype_name, COUNT(*) AS assets_count, SUM(purchase_cost) AS sum_purchase_cost').joins(:organization, asset_subtype: :asset_type).where(organization_id: @organization_list).group(:organization_id, :asset_type_id).to_sql)
-
level = 'type'
-
else
-
asset_subtype_ids = AssetType.includes(:asset_subtypes).find_by(id: asset_type_id).asset_subtypes.ids
-
results = ActiveRecord::Base.connection.exec_query(Rails.application.config.asset_base_class_name.constantize.operational.select('organization_id, asset_subtype_id, organizations.short_name AS org_short_name, asset_subtypes.name AS subtype_name, COUNT(*) AS assets_count, SUM(purchase_cost) AS sum_purchase_cost').joins(:organization, :asset_subtype).where(organization_id: (params[:org] || @organization_list), asset_subtype_id: asset_subtype_ids).group(:organization_id, :asset_subtype_id).to_sql)
-
level = 'subtype'
-
end
-
-
respond_to do |format|
-
format.js {
-
if params[:org]
-
render partial: 'dashboards/assets_widget_table_rows', locals: {results: results, level: level }
-
else
-
render partial: 'dashboards/assets_widget_table', locals: {results: results, level: level }
-
end
-
}
-
end
-
end
-
-
-
-
# renders either a table or map view of a selected list of assets
-
#
-
# Parameters include asset_type, asset_subtype, id_list, box, or search_text
-
#
-
1
def index
-
-
1
add_index_breadcrumbs
-
-
# disable any spatial filters for this view
-
1
@spatial_filter = nil
-
-
1
@assets = get_assets
-
-
1
terminal_crumb = nil
-
1
if @early_disposition
-
terminal_crumb = "Early disposition proposed"
-
1
elsif @transferred_assets
-
terminal_crumb = "Transferred Assets"
-
1
elsif @asset_group.present?
-
asset_group = AssetGroup.find_by_object_key(@asset_group)
-
terminal_crumb = asset_group
-
1
elsif @search_text.present?
-
terminal_crumb = "Search '#{@search_text}'"
-
1
elsif @asset_subtype > 0
-
subtype = AssetSubtype.find(@asset_subtype)
-
add_breadcrumb subtype.asset_type.name.pluralize(2), inventory_index_path(:asset_type => subtype.asset_type, :asset_subtype => 0)
-
terminal_crumb = subtype.name
-
1
elsif @manufacturer_id > 0
-
add_breadcrumb "Manufacturers", manufacturers_path
-
manufacturer = Manufacturer.find(@manufacturer_id)
-
terminal_crumb = manufacturer.name
-
1
elsif @asset_type > 0
-
asset_type = AssetType.find(@asset_type)
-
terminal_crumb = asset_type.name.pluralize(2)
-
-
end
-
1
add_breadcrumb terminal_crumb if terminal_crumb
-
-
# check that an order param was provided otherwise use asset_tag as the default
-
1
params[:sort] ||= 'transam_assets.asset_tag'
-
-
# fix sorting on organizations to be alphabetical not by index
-
1
params[:sort] = 'organizations.short_name' if params[:sort] == 'organization_id'
-
-
1
respond_to do |format|
-
1
format.html
-
1
format.js
-
1
format.json {
-
render :json => {
-
:total => @assets.count,
-
:rows => index_rows_as_json
-
}
-
}
-
1
format.xls do
-
filename = (terminal_crumb || "unknown").gsub(" ", "_").underscore
-
response.headers['Content-Disposition'] = "attachment; filename=#{filename}.xls"
-
end
-
1
format.xlsx do
-
filename = (terminal_crumb || "unknown").gsub(" ", "_").underscore
-
response.headers['Content-Disposition'] = "attachment; filename=#{filename}.xlsx"
-
end
-
end
-
end
-
-
1
def index_rows_as_json
-
multi_sort = params[:multiSort]
-
-
unless (multi_sort.nil?)
-
sorting_string = ""
-
-
multi_sort.each { |x|
-
sorting_string = sorting_string + "#{x[0]}: :#{x[1]}"
-
}
-
-
else
-
sorting_string = "#{params[:sort]} #{params[:order]}"
-
end
-
-
cache_list(@assets.order(sorting_string), INDEX_KEY_LIST_VAR)
-
-
@assets.order(sorting_string).limit(params[:limit]).offset(params[:offset]).as_json(user: current_user, include_early_disposition: @early_disposition)
-
-
end
-
-
1
def fire_asset_event_workflow_events
-
-
event_name = params[:event]
-
asset_event_type = AssetEventType.find_by(id: params[:asset_event_type_id])
-
-
if asset_event_type && params[:targets]
-
-
notification_enabled = asset_event_type.class_name.constantize.workflow_notification_enabled?
-
-
events = asset_event_type.class_name.constantize.where(object_key: params[:targets].split(','))
-
-
failed = 0
-
events.each do |evt|
-
-
if evt.fire_state_event(event_name)
-
workflow_event = WorkflowEvent.new
-
workflow_event.creator = current_user
-
workflow_event.accountable = evt
-
workflow_event.event_type = event_name
-
workflow_event.save
-
-
if notification_enabled
-
evt.notify_event_by(current_user, event_name)
-
end
-
-
else
-
failed += 1
-
end
-
end
-
-
end
-
-
redirect_back(fallback_location: root_path)
-
end
-
-
# makes a copy of an asset and renders it. The new asset is not saved
-
# and has any identifying chracteristics identified as CLEANSABLE_FIELDS are nilled
-
1
def copy
-
-
# add_breadcrumb "#{@asset.asset_type.name}".pluralize(2), inventory_index_path(:asset_type => @asset.asset_type, :asset_subtype => 0)
-
# add_breadcrumb "#{@asset.asset_subtype.name}", inventory_index_path(:asset_subtype => @asset.asset_subtype)
-
# add_breadcrumb @asset.asset_tag, inventory_path(@asset)
-
# add_breadcrumb "Copy"
-
-
# create a copy of the asset and null out all the fields that are identified as cleansable
-
new_asset = @asset.copy(true)
-
-
notify_user(:notice, "Complete the master record to copy Asset #{@asset.to_s}.")
-
-
@asset = new_asset
-
-
# render the edit view
-
respond_to do |format|
-
format.html { render :action => "edit" }
-
format.json { render :json => @asset }
-
end
-
-
end
-
-
# not used
-
1
def summary_info
-
-
respond_to do |format|
-
format.js # show.html.erb
-
format.json { render :json => @asset }
-
end
-
-
end
-
-
1
def show
-
-
add_breadcrumbs
-
-
# add_breadcrumb "#{@asset.asset_subtype.name}", inventory_index_path(:asset_subtype => @asset.asset_subtype)
-
# add_breadcrumb @asset.asset_tag, inventory_path(@asset)
-
-
# Set the asset class view var. This can be used to determine which view components
-
# are rendered, for example, which tabs and action items the user sees
-
@asset_class_name = @asset.class.name.underscore
-
-
# get the @prev_record_path and @next_record_path view vars
-
get_next_and_prev_object_keys(@asset, INDEX_KEY_LIST_VAR)
-
@prev_record_path = @prev_record_key.nil? ? "#" : inventory_path(@prev_record_key, use_last_tab: 1)
-
@next_record_path = @next_record_key.nil? ? "#" : inventory_path(@next_record_key, use_last_tab: 1)
-
-
respond_to do |format|
-
format.html # show.html.erb
-
format.json { render :json => @asset }
-
end
-
-
end
-
-
1
def edit
-
-
# add_breadcrumb "#{@asset.asset_type.name}".pluralize(2), inventory_index_path(:asset_type => @asset.asset_type, :asset_subtype => 0)
-
# add_breadcrumb "#{@asset.asset_subtype.name}", inventory_index_path(:asset_subtype => @asset.asset_subtype)
-
# When editing a newly transferred asset this link is invalid so we don't want to show it.
-
if @asset.asset_tag == @asset.object_key
-
@asset.asset_tag = nil
-
else
-
add_breadcrumb @asset.asset_tag, inventory_path(@asset)
-
end
-
add_breadcrumb "Update master record", edit_inventory_path(@asset)
-
-
end
-
-
1
def update
-
-
#add_breadcrumb "#{@asset.asset_type.name}".pluralize(2), inventory_index_path(:asset_type => @asset.asset_type, :asset_subtype => 0)
-
#add_breadcrumb @asset.name, inventory_path(@asset)
-
#add_breadcrumb "Modify", edit_inventory_path(@asset)
-
-
# transfered assets need to remove notification if exists
-
# if @asset.asset_tag == @asset.object_key
-
# notification = Notification.where("text = 'A new asset has been transferred to you. Please update the asset.' AND link LIKE ?" , "%#{@asset.object_key}%").first
-
# notification.update(active: false) if notification.present?
-
# end
-
-
respond_to do |format|
-
if @asset.update_attributes(new_form_params(@asset))
-
-
# If the asset was successfully updated, schedule update the condition and disposition asynchronously
-
#Delayed::Job.enqueue AssetUpdateJob.new(@asset.asset.object_key), :priority => 0
-
# See if this asset has any dependents that use its spatial reference
-
if @asset.geometry and @asset.occupants.count > 0
-
# schedule an update to the spatial references of the dependent assets
-
Delayed::Job.enqueue AssetDependentSpatialReferenceUpdateJob.new(@asset.object_key), :priority => 0
-
end
-
notify_user(:notice, "Asset #{@asset.to_s} was successfully updated.")
-
-
format.html { redirect_to inventory_url(@asset, use_last_tab: 1) }
-
format.js { notify_user(:notice, "#{@asset} successfully updated.") }
-
format.json { head :no_content }
-
else
-
format.html { render :action => "edit" }
-
format.js { render :action => "edit" }
-
format.json { render :json => @asset.errors, :status => :unprocessable_entity }
-
end
-
end
-
end
-
-
1
def get_subheader
-
respond_to do |format|
-
format.js
-
end
-
end
-
-
1
def get_dependents
-
respond_to do |format|
-
format.js
-
format.json { render :json => @asset.to_json }
-
end
-
end
-
-
1
def add_dependents
-
params[:asset][:dependents_attributes].each do |key, val|
-
unless val[:id]
-
dependent = TransamAsset.find_by(object_key: val[:object_key])
-
if dependent
-
@asset.dependents << dependent
-
@asset.update_condition # might need to change to run full AssetUpdateJob
-
end
-
end
-
end
-
-
redirect_back(fallback_location: root_path)
-
end
-
-
1
def get_dependent_subform
-
@dependent_subform_target_model = params[:dependent_subform_target_model]
-
-
@dependent = @dependent_subform_target_model.constantize.find_by(object_key: params[:dependent_object_key])
-
-
@dependent_subform_target = params[:dependent_subform_target]
-
@dependent_subform_view = params[:dependent_subform_view]
-
-
respond_to do |format|
-
format.js
-
end
-
-
end
-
-
1
def new_asset
-
authorize! :new, Rails.application.config.asset_base_class_name.constantize
-
-
add_breadcrumb "Add Asset", new_asset_inventory_index_path
-
-
end
-
-
1
def new
-
-
asset_class_name = params[:asset_seed_class_name] || 'AssetType'
-
-
@asset_class_instance = asset_class_name.constantize.find_by(id: params[:asset_base_class_id])
-
if @asset_class_instance.nil?
-
notify_user(:alert, "Asset class '#{params[:asset_base_class_id]}' not found. Can't create new asset!")
-
redirect_to(root_url)
-
return
-
end
-
-
#add_breadcrumb "#{asset_subtype.asset_type.name}".pluralize(2), inventory_index_path(:asset_type => asset_subtype.asset_type)
-
#add_breadcrumb "#{asset_subtype.name}", inventory_index_path(:asset_subtype => asset_subtype)
-
#add_breadcrumb "New", new_inventory_path(asset_subtype)
-
-
# Use the asset class to create an asset of the correct type
-
@asset = Rails.application.config.asset_base_class_name.constantize.new_asset(@asset_class_instance, params)
-
-
# See if the user selected an org to associate the asset with
-
if params[:organization_id].present?
-
@asset.organization = Organization.find(params[:organization_id])
-
else
-
@asset.organization_id = @organization_list.first
-
end
-
-
if params[:parent_id].present?
-
@asset.parent_id = params[:parent_id].to_i
-
end
-
-
respond_to do |format|
-
format.html # new.html.haml this had been an erb and is now an haml the change should just be caught
-
format.json { render :json => @asset }
-
end
-
end
-
-
1
def create
-
-
asset_class_name = params[:asset_seed_class_name] || 'AssetType'
-
if asset_class_name == 'AssetType' && params[:asset][:asset_type_id].blank?
-
asset_subtype = AssetSubtype.find_by(id: params[:asset][:asset_subtype_id])
-
asset_class_instance = asset_class_name.constantize.find_by(id: asset_subtype.try(:asset_type_id))
-
else
-
asset_class_instance = asset_class_name.constantize.find_by(id: params[:asset][asset_class_name.foreign_key.to_sym])
-
end
-
-
if asset_class_instance.nil?
-
notify_user(:alert, "Asset class '#{params[:asset][asset_class_name.foreign_key.to_sym]}' not found. Can't create new asset!")
-
redirect_to(root_url)
-
return
-
end
-
-
# Use the asset class to create an asset of the correct type
-
@asset = Rails.application.config.asset_base_class_name.constantize.new_asset(asset_class_instance, params)
-
@asset.attributes = new_form_params(@asset)
-
-
-
# If the asset does not have an org already defined, set to the default for
-
# the user
-
if @asset.organization.blank?
-
@asset.organization_id = @organization_list.first
-
end
-
-
#@asset.creator = current_user
-
#@asset.updator = current_user
-
-
#Rails.logger.debug @asset.inspect
-
-
# add_breadcrumb "#{asset_type.name}".pluralize(2), inventory_index_path(:asset_type => asset_subtype.asset_type)
-
# add_breadcrumb "#{asset_subtype.name}", inventory_index_path(:asset_subtype => asset_subtype)
-
# add_breadcrumb "New", new_inventory_path(asset_subtype)
-
-
respond_to do |format|
-
if @asset.save
-
-
# If the asset was successfully saved, schedule update the condition and disposition asynchronously
-
#Delayed::Job.enqueue AssetUpdateJob.new(@asset.object_key), :priority => 0
-
-
notify_user(:notice, "Asset #{@asset.to_s} was successfully created.")
-
-
format.html { redirect_to inventory_url(@asset) }
-
format.json { render :json => @asset, :status => :created, :location => @asset }
-
else
-
#Rails.logger.debug @asset.errors.inspect
-
format.html { render :action => "new" }
-
format.json { render :json => @asset.errors, :status => :unprocessable_entity }
-
end
-
end
-
end
-
-
# called when the user wants to delete an asset
-
1
def destroy
-
-
# make sure we can find the asset we are supposed to be removing and that it belongs to us.
-
if @asset.nil?
-
redirect_to(root_path, :flash => { :alert => t(:error_404) })
-
return
-
end
-
-
# Destroy this asset, call backs to remove each associated object will be made
-
@asset.destroy
-
-
notify_user(:notice, "Asset was successfully removed.")
-
-
respond_to do |format|
-
format.html { redirect_to(inventory_index_url(@asset.class.asset_seed_class_name.foreign_key => @asset.send(@asset.class.asset_seed_class_name.foreign_key))) }
-
format.json { head :no_content }
-
end
-
-
end
-
-
# Adds the assets to the user's tag list or removes it if the asset
-
# is already tagged. called by ajax so no response is rendered
-
# NOT USED
-
1
def tag
-
-
if @asset.tagged? current_user
-
@asset.users.delete current_user
-
else
-
@asset.tag current_user
-
end
-
-
# No response needed
-
render :nothing => true
-
-
end
-
-
# Called via AJAX to get dynamic content via AJAX
-
# NOT USED (I think)
-
1
def popup
-
-
str = ""
-
if @asset
-
str = render_to_string(:partial => "popup", :locals => { :asset => @asset })
-
end
-
-
render json: str.to_json
-
-
end
-
-
#------------------------------------------------------------------------------
-
#
-
# Protected Methods
-
#
-
#------------------------------------------------------------------------------
-
1
protected
-
#
-
# Sets the view variables. This is used to set up the vars before a call to get_assets
-
# @asset_type
-
# @asset_subtype
-
# @search_text
-
# @id_filter_list
-
# @spatial_filter
-
# @fmt
-
# @view
-
# @asset_class_name
-
# @filter
-
#
-
1
def set_view_vars
-
-
# Check to see if we got an asset group to sub select on. This occurs when the user
-
# selects an asset group from the menu selector
-
1
if params[:asset_group].nil? or params[:asset_group] == '0'
-
1
@asset_group = ''
-
else
-
@asset_group = params[:asset_group]
-
end
-
-
# Check to see if we got an asset type to sub select on. This occurs when the user
-
# selects an asset type from the drop down
-
1
if params[:asset_type].nil?
-
1
@asset_type = 0
-
else
-
@asset_type = params[:asset_type].to_i
-
end
-
-
# Check to see if we got an asset subtype to sub select on. This will happen if an asset type is selected
-
# already and the user selected a subtype from the dropdown.
-
1
if params[:asset_subtype].nil?
-
1
@asset_subtype = 0
-
else
-
@asset_subtype = params[:asset_subtype].to_i
-
end
-
-
# Check to see if we got an organization to sub select on.
-
1
if params[:org_id].nil?
-
1
@org_id = 0
-
else
-
@org_id = params[:org_id].to_i
-
end
-
-
# Check to see if we got a manufacturer to sub select on.
-
1
if params[:manufacturer_id].nil?
-
1
@manufacturer_id = 0
-
else
-
@manufacturer_id = params[:manufacturer_id].to_i
-
end
-
-
# Check to see if we got a service status to sub select on.
-
1
if params[:service_status].nil?
-
1
@service_status = 0
-
else
-
@service_status = params[:service_status].to_i
-
end
-
-
# Check to see if we got a search text and search param to filter on
-
1
if params[:search_text].nil?
-
# See if one is stored in the session
-
1
@search_text = session[:search_text].blank? ? nil : session[:search_text]
-
1
@search_param = session[:search_param].blank? ? nil : session[:search_param]
-
else
-
@search_text = params[:search_text]
-
@search_param = params[:search_param]
-
end
-
-
# Check to see if we got list of assets to filter on
-
1
if params[:ids]
-
#Checks to see if the id list is already an array. Converts a string to
-
# an array if necessary.
-
if params[:ids].is_a?(Array)
-
@id_filter_list = params[:ids]
-
else
-
@id_filter_list = params[:ids].split("|")
-
end
-
-
else
-
1
@id_filter_list = []
-
end
-
-
# Check to see if we got spatial filter. This session variable is managed
-
# by the spatial_filter method
-
1
@spatial_filter = session[:spatial_filter]
-
-
# Check to see if we got a different format to render
-
1
if params[:format]
-
@fmt = params[:format]
-
else
-
1
@fmt = 'html'
-
end
-
-
# Check to see if search for early disposition proposed assets only
-
1
if params[:early_disposition] == '1'
-
@early_disposition = true
-
end
-
1
if params[:transferred_assets] == '1'
-
@transferred_assets = true
-
end
-
-
# If the asset type is not set we default to the asset base class
-
1
if @id_filter_list.present? or (@asset_type == 0)
-
# THIS WILL NO LONGER WORK
-
# asset base class name should really be seed to pull typed asset class
-
# base class here is just Asset or the new TransamAsset
-
1
@asset_class_name = 'TransamAsset'
-
elsif @asset_subtype > 0
-
# we have an asset subtype so get it and get the asset type from it. We also set the filter form
-
# to the name of the selected subtype
-
subtype = AssetSubtype.find(@asset_subtype)
-
@asset_type = subtype.asset_type.id
-
@asset_class_name = subtype.asset_type.class_name
-
@filter = subtype.name
-
else
-
asset_type = AssetType.find(@asset_type)
-
@asset_class_name = asset_type.class_name
-
end
-
1
@view = "#{@asset_class_name.underscore}_index"
-
-
1
Rails.logger.debug "@fmt = #{@fmt}"
-
1
Rails.logger.debug "@asset_type = #{@asset_type}"
-
1
Rails.logger.debug "@asset_subtype = #{@asset_subtype}"
-
1
Rails.logger.debug "@asset_class_name = #{@asset_class_name}"
-
1
Rails.logger.debug "@view = #{@view}"
-
1
Rails.logger.debug "@view = #{@fta_asset_class_id}"
-
-
end
-
-
1
def add_index_breadcrumbs
-
# placeholder
-
end
-
-
1
def add_breadcrumbs
-
add_breadcrumb "#{@asset.asset_type.name}".pluralize, inventory_index_path(:asset_type => @asset.asset_type, :asset_subtype => 0)
-
add_breadcrumb "#{@asset.asset_type.name.singularize} Profile"
-
end
-
-
# returns a list of assets for an index view (index, map) based on user selections. Called after
-
# a call to set_view_vars
-
1
def get_assets
-
-
# Create a class instance of the asset type which can be used to perform
-
# active record queries
-
1
klass = Object.const_get @asset_class_name
-
1
@asset_class = klass.name
-
-
# here we build the query one clause at a time based on the input params
-
1
clauses = {}
-
1
unless @org_id == 0
-
clauses[:organization_id] = @org_id
-
else
-
1
clauses[:organization_id] = @organization_list
-
end
-
-
1
unless @manufacturer_id == 0
-
clauses[:manufacturer_id] = @manufacturer_id
-
end
-
-
1
if @asset_class_name.constantize.new.respond_to? :disposition_date
-
1
if @disposition_year.blank?
-
1
clauses[:disposition_date] = nil
-
else
-
clauses['YEAR(disposition_date)'] = @disposition_year
-
end
-
end
-
-
-
1
unless @search_text.blank?
-
search_clauses = []
-
search_values = []
-
# get the list of searchable fields from the asset class
-
searchable_fields = klass.new.searchable_fields
-
# create an OR query for each field
-
query_str = []
-
first = true
-
# parameterize the search based on the selected search parameter
-
search_value = get_search_value(@search_text, @search_param)
-
# Construct the query based on the searchable fields for the model
-
searchable_fields.each do |field|
-
if first
-
first = false
-
query_str << '('
-
else
-
query_str << ' OR '
-
end
-
-
query_str << field
-
query_str << ' LIKE ? '
-
# add the value in for this sub clause
-
search_values << search_value
-
end
-
query_str << ')' unless searchable_fields.empty?
-
-
search_clauses << [query_str.join]
-
-
klass = klass.where(search_clauses.join(' AND '), *search_values)
-
end
-
-
1
unless @id_filter_list.blank?
-
clauses[:object_key] = @id_filter_list
-
end
-
-
1
unless @asset_subtype == 0
-
clauses[:asset_subtype_id] = [@asset_subtype]
-
end
-
-
1
unless @asset_type == 0
-
clauses[:asset_types] = {id: @asset_type}
-
end
-
-
1
if klass.respond_to? :in_transfer
-
1
klass = @transferred_assets ? klass.in_transfer : klass.not_in_transfer
-
end
-
-
1
unless @spatial_filter.blank?
-
gis_service = GisService.new
-
search_box = gis_service.search_box_from_bbox(@spatial_filter)
-
wkt = "#{search_box.as_wkt}"
-
-
klass = klass.where('MBRContains(GeomFromText("' + wkt + '"), geometry) = ?', 1)
-
end
-
-
# See if we got an asset group. If we did then we can
-
# use the asset group collection to filter on instead of creating
-
# a new query. This is kind of a hack but works!
-
1
unless @asset_group.blank?
-
asset_group = AssetGroup.find_by_object_key(@asset_group)
-
klass = asset_group.assets unless asset_group.nil?
-
end
-
-
# Search for only early dispostion proposed assets if flag is on
-
1
if @early_disposition
-
klass = klass.joins(:early_disposition_requests).where(asset_events: {state: 'new'})
-
end
-
-
# send the query
-
1
if @asset_class_name == 'TransamAsset'
-
1
klass = klass.includes({asset_subtype: :asset_type},:organization, :manufacturer)
-
else
-
join_relations = klass.actable_hierarchy
-
join_relations[join_relations.key(:transam_asset)] = {transam_asset: [{asset_subtype: :asset_type},:organization, :manufacturer]}
-
klass = klass.includes(join_relations)
-
end
-
1
klass.where(clauses)
-
end
-
-
# stores the just-created list of asset ids in the session
-
1
def cache_assets(assets)
-
list = []
-
unless assets.nil?
-
assets.each do |a|
-
list << a.object_key
-
end
-
end
-
cache_objects(ASSET_KEY_LIST_VAR, list)
-
end
-
-
#------------------------------------------------------------------------------
-
#
-
# Private Methods
-
#
-
#------------------------------------------------------------------------------
-
1
private
-
-
# Never trust parameters from the scary internet, only allow the white list through.
-
1
def form_params
-
params.require(:asset).permit(asset_allowable_params)
-
end
-
-
1
def new_form_params(asset)
-
params.require(:asset).permit(asset.allowable_params)
-
end
-
-
#
-
# Overrides the utility method in the base class
-
#
-
1
def get_selected_asset(convert=true)
-
selected_asset = Rails.application.config.asset_base_class_name.constantize.find_by(:organization_id => @organization_list, :object_key => params[:id]) unless params[:id].blank?
-
if convert
-
asset = Rails.application.config.asset_base_class_name.constantize.get_typed_asset(selected_asset)
-
else
-
asset = selected_asset
-
end
-
-
-
return asset
-
end
-
-
1
def reformat_date(date_str)
-
# See if it's already in iso8601 format first
-
return date_str if date_str.match(/\A\d{4}-\d{2}-\d{2}\z/)
-
-
Date.strptime(date_str, '%m/%d/%Y').strftime('%Y-%m-%d')
-
end
-
-
1
def reformat_date_fields
-
params[:asset][:purchase_date] = reformat_date(params[:asset][:purchase_date]) unless params[:asset][:purchase_date].blank?
-
params[:asset][:in_service_date] = reformat_date(params[:asset][:in_service_date]) unless params[:asset][:in_service_date].blank?
-
params[:asset][:warranty_date] = reformat_date(params[:asset][:warranty_date]) unless params[:asset][:warranty_date].blank?
-
end
-
-
# Manage the vendor_id/vendor_name
-
1
def update_vendor_param
-
-
# If the vendor_name is set in the params then the model needs to override the
-
# vendor_id param. If both the vendor_id and vendor_name are unset then the
-
# model needs to remove the vendor. If the vendor_id is set, leave it alone
-
if params[:asset][:vendor_name].present?
-
vendor = Vendor.find_or_create_by(:name => params[:asset][:vendor_name], :organization => @organization)
-
params[:asset][:vendor_id] = vendor.id
-
elsif params[:asset][:vendor_id].blank? and params[:asset][:vendor_name].blank?
-
params[:asset][:vendor_id] = nil
-
end
-
# vendor_name has served its purpose (find/set vendor_id), so remove from hash
-
params[:asset].delete :vendor_name
-
-
end
-
end
-
class ClientAdminController < OrganizationAwareController
-
add_breadcrumb 'Home', :root_path
-
-
# GET /client_admin
-
def index
-
add_breadcrumb 'Client Admin Interface', client_admin_path
-
end
-
-
end
-
1
class CommentsController < NestedResourceController
-
1
before_action :set_comment, :only => [:edit, :update, :destroy]
-
-
# Lock down the controller
-
1
authorize_resource except: [:show]
-
-
# GET /comments
-
# GET /comments.json
-
1
def index
-
-
1
@commentable = find_resource
-
1
@comments = @commentable.comments
-
-
end
-
-
# GET /comments/1
-
# GET /comments/1.json
-
1
def show
-
end
-
-
# GET /comments/new
-
1
def new
-
1
@comment = Comment.new
-
1
@commentable = find_resource
-
end
-
-
# GET /comments/1/edit
-
1
def edit
-
-
1
@commentable = @comment.commentable
-
-
end
-
-
# POST /comments
-
# POST /comments.json
-
1
def create
-
1
@commentable = find_resource
-
1
@comment = @commentable.comments.build(comment_params)
-
1
@comment.creator = current_user
-
-
1
respond_to do |format|
-
1
if @comment.save
-
1
notify_user(:notice, 'Comment was successfully created.')
-
2
format.html { redirect_back(fallback_location: root_path) }
-
1
format.json { render action: 'show', status: :created, location: @comment }
-
else
-
format.html { render action: 'new' }
-
format.json { render json: @comment.errors, status: :unprocessable_entity }
-
end
-
end
-
end
-
-
# PATCH/PUT /comments/1
-
# PATCH/PUT /comments/1.json
-
1
def update
-
-
1
@commentable = @comment.commentable
-
-
1
respond_to do |format|
-
1
if @comment.update(comment_params)
-
1
notify_user(:notice, 'Comment was successfully updated.')
-
2
format.html { redirect_to get_resource_url(@commentable) }
-
1
format.json { head :no_content }
-
else
-
format.html { render action: 'edit' }
-
format.json { render json: @comment.errors, status: :unprocessable_entity }
-
end
-
end
-
end
-
-
# DELETE /comments/1
-
# DELETE /comments/1.json
-
1
def destroy
-
-
1
@commentable = @comment.commentable
-
1
@comment.destroy
-
-
1
notify_user(:notice, 'Comment was successfully removed.')
-
1
respond_to do |format|
-
2
format.html { redirect_back(fallback_location: root_path) }
-
1
format.json { head :no_content }
-
end
-
end
-
-
1
private
-
-
# Use callbacks to share common setup or constraints between actions.
-
1
def set_comment
-
3
@comment = Comment.find_by(:object_key => params[:id])
-
3
if @comment.nil?
-
redirect_to '/404'
-
return
-
end
-
end
-
-
# Never trust parameters from the scary internet, only allow the white list through.
-
1
def comment_params
-
2
params.require(:comment).permit(Comment.allowable_params)
-
end
-
-
end
-
1
module JsonResponseHelper
-
-
-
# Formats a JSON response using the JSend specification
-
1
def json_response(status, body={})
-
14
body = body.with_indifferent_access
-
14
return body.merge({status: status.to_s})
-
end
-
-
1
module ApiErrorCatcher
-
-
# Catch internal errors gracefully and send back a 500 response with JSON and proper headers
-
1
def self.included(base)
-
# Catches server errors so that response can be rendered as JSON with proper headers, etc.
-
1
if base < ActionController::API
-
1
base.rescue_from Exception, with: :api_error_response
-
end
-
-
1
base.include JsonResponseHelper
-
end
-
-
# Rescues 500 errors and renders them properly as JSON response
-
1
def api_error_response(exception)
-
exception.backtrace.each { |line| logger.error line }
-
error_response = {
-
message: exception.message,
-
data: { type: exception.class.name, message: exception.message, backtrace: exception.backtrace}
-
}
-
render status: 500, json: json_response(:error, error_response)
-
end
-
-
-
-
end
-
-
# For catching JSON parser errors when client sends an invalid JSON request
-
# This class is applied to the middleware stack in application.rb
-
1
class CatchJsonParseErrors
-
1
include JsonResponseHelper
-
-
1
def initialize(app)
-
1
@app = app
-
end
-
-
36
def call(env)
-
begin
-
35
@app.call(env)
-
rescue ActionDispatch::Http::Parameters::ParseError => error
-
if env['CONTENT_TYPE'] =~ /application\/json/
-
error_output = {request: "There was a problem in the JSON you submitted.", error: error}
-
return [
-
400, {"Content-Type" => "application/json; charset=utf-8"},
-
[ json_response(:fail, data: error_output).to_json ]
-
]
-
else
-
raise error
-
end
-
end
-
end
-
end
-
-
-
end
-
1
class DashboardsController < OrganizationAwareController
-
-
1
add_breadcrumb "Home", :root_path
-
-
1
def index
-
-
1
@queues = []
-
-
1
@queues << {:name => "Tagged Assets", :view => 'assets_queue', :objs => current_user.assets.where(organization_id: @organization_list)}
-
-
1
respond_to do |format|
-
1
format.html
-
end
-
-
end
-
-
end
-
1
class DocumentsController < NestedResourceController
-
1
before_action :set_document, :only => [:edit, :update, :destroy, :download]
-
-
# Lock down the controller
-
1
authorize_resource only: [:index, :new, :create, :edit, :update, :destroy]
-
-
# GET /documents
-
# GET /documents.json
-
1
def index
-
-
1
@documentable = find_resource
-
1
@documents = @documentable.documents
-
-
end
-
-
# GET /documents/new
-
1
def new
-
1
@document = Document.new
-
1
@documentable = find_resource
-
end
-
-
# GET /documents/1/edit
-
1
def edit
-
-
1
@documentable = @document.documentable
-
-
end
-
-
1
def download
-
-
if @document.nil?
-
notify_user(:alert, 'Record not found!')
-
redirect_to( root_path )
-
return
-
end
-
# read the attachment
-
content = open(@document.document.url, "User-Agent" => "Ruby/#{RUBY_VERSION}") {|f| f.read}
-
# Send to the client
-
send_data content, :filename => @document.original_filename
-
-
end
-
-
# POST /documents
-
# POST /documents.json
-
1
def create
-
1
@documentable = find_resource
-
1
@document = @documentable.documents.build(document_params)
-
1
@document.creator = current_user
-
-
1
respond_to do |format|
-
1
if @document.save
-
1
notify_user(:notice, 'Document was successfully created.')
-
2
format.html { redirect_back(fallback_location: root_path) }
-
1
format.json { render action: 'show', status: :created, location: @document }
-
else
-
format.html { render action: 'new' }
-
format.json { render json: @document.errors, status: :unprocessable_entity }
-
end
-
end
-
end
-
-
# PATCH/PUT /documents/1
-
# PATCH/PUT /documents/1.json
-
1
def update
-
-
1
@documentable = @document.documentable
-
-
1
respond_to do |format|
-
1
if @document.update(document_params)
-
1
notify_user(:notice, 'Document was successfully updated.')
-
2
format.html { redirect_to get_resource_url(@documentable) }
-
1
format.json { head :no_content }
-
else
-
format.html { render action: 'edit' }
-
format.json { render json: @document.errors, status: :unprocessable_entity }
-
end
-
end
-
end
-
-
# DELETE /documents/1
-
# DELETE /documents/1.json
-
1
def destroy
-
-
1
@documentable = @document.documentable
-
1
@document.destroy
-
-
1
notify_user(:notice, 'Document was successfully removed.')
-
1
respond_to do |format|
-
2
format.html { redirect_back(fallback_location: root_path) }
-
1
format.json { head :no_content }
-
end
-
end
-
-
1
private
-
-
# Use callbacks to share common setup or constraints between actions.
-
1
def set_document
-
3
@document = Document.find_by(:object_key => params[:id])
-
3
if @document.nil?
-
redirect_to '/404'
-
return
-
end
-
end
-
-
# Never trust parameters from the scary internet, only allow the white list through.
-
1
def document_params
-
2
params.require(:document).permit(Document.allowable_params)
-
end
-
-
end
-
1
class ErrorsController < TransamController
-
-
1
layout "errored"
-
-
1
def show
-
1
render "#{status_code}.html.haml", :status => status_code
-
end
-
-
1
def system_health
-
# send metric to AWS for the number of delayed jobs currently running
-
PutMetricDataService.new.put_metric('DelayedJobsRunning', 'Count', Delayed::Job.count)
-
-
# return 500 if delayed job been locked for at least 8 hours
-
if Delayed::Job.where('locked_at < ?', DateTime.now-8.hours).count > 0
-
redirect_to '/500'
-
end
-
end
-
-
1
protected
-
-
1
def status_code
-
2
params[:code] || 500
-
end
-
-
end
-
#-------------------------------------------------------------------------------
-
# FormAwareController
-
#
-
# Abstract controller that is used as the base class for any concrete controllers
-
# that are based on a form
-
#
-
#-------------------------------------------------------------------------------
-
1
class FormAwareController < OrganizationAwareController
-
-
# set the @form_class variable before any actions are invoked
-
1
before_action :get_form_class
-
-
#-------------------------------------------------------------------------------
-
# Used by all form controllers to update the form status as it goes thorugh
-
# its workflows
-
#-------------------------------------------------------------------------------
-
1
def fire_workflow_event
-
-
# Check that this is a valid event name for the state machines
-
if @form.class.event_names.include? params[:event]
-
event_name = params[:event]
-
if @form.fire_state_event(event_name)
-
event = WorkflowEvent.new
-
event.creator = current_user
-
event.accountable = @form
-
event.event_type = event_name
-
event.save
-
-
# if notification enabled, then send out
-
if @form.class.try(:workflow_notification_enabled?)
-
@form.notify_event_by(current_user, event_name)
-
end
-
else
-
notify_user(:alert, "Could not #{event_name.humanize} form #{@form}")
-
end
-
else
-
notify_user(:alert, "#{params[:event_name]} is not a valid event for a #{@form.class.name}")
-
end
-
-
redirect_back(fallback_location: root_path)
-
-
end
-
-
#-------------------------------------------------------------------------------
-
1
protected
-
#-------------------------------------------------------------------------------
-
-
#-------------------------------------------------------------------------------
-
# Returns the selected form
-
#-------------------------------------------------------------------------------
-
1
def get_form_class
-
# load this report and create the report instance
-
3
form = Form.find_by(:object_key => params[:form_id]) unless params[:form_id].blank?
-
# check that the current use is authorized to view the report
-
3
unless form.nil?
-
2
if current_user.is_in_roles? form.role_names
-
1
@form_type = form
-
end
-
end
-
3
if @form_type.nil?
-
2
notify_user(:alert, "Can't find form.")
-
2
redirect_to '/404'
-
return
-
end
-
end
-
-
end
-
1
class FormsController < OrganizationAwareController
-
-
1
before_action :get_form, :only => [:show]
-
-
1
add_breadcrumb "Home", :root_path
-
1
add_breadcrumb "Forms", :forms_path
-
-
# Lock down the controller
-
1
authorize_resource only: [:index]
-
-
#-----------------------------------------------------------------------------
-
1
def index
-
-
1
@forms = []
-
1
Form.all.each do |form|
-
4
if current_user.is_in_roles? form.role_names
-
2
@forms << form
-
end
-
end
-
-
end
-
-
#-----------------------------------------------------------------------------
-
# The show method for a form simply redirects the view
-
# to the controller configured for the selected form type
-
#-----------------------------------------------------------------------------
-
1
def show
-
-
path = eval("form_#{@form.controller}_url('#{@form.object_key}')")
-
redirect_to path
-
-
end
-
-
#-----------------------------------------------------------------------------
-
# Private Methods
-
#-----------------------------------------------------------------------------
-
1
private
-
-
#-----------------------------------------------------------------------------
-
# Returns the selected form
-
#-----------------------------------------------------------------------------
-
1
def get_form
-
# load this report and create the form instance
-
1
form = Form.find_by(:object_key => params[:id])
-
# check that the current use is authorized to view the form
-
1
unless form.nil?
-
if current_user.is_in_roles? form.role_names
-
@form = form
-
end
-
end
-
1
if @form.nil?
-
1
notify_user(:alert, "Can't find form.")
-
1
redirect_to '/404'
-
return
-
end
-
end
-
end
-
1
class ImagesController < NestedResourceController
-
1
before_action :set_image, :only => [:edit, :update, :destroy, :download]
-
-
# Lock down the controller
-
1
authorize_resource only: [:index, :new, :create, :edit, :update, :destroy]
-
-
# GET /images
-
# GET /images.json
-
1
def index
-
1
if params[:global_base_imagable]
-
@imagable = GlobalID::Locator.locate(GlobalID.parse(params[:global_base_imagable]))
-
@images = Image.where(base_imagable: @imagable)
-
1
elsif params[:global_any_imagable] # parameter to return images of self as parent and children
-
@imagable = GlobalID::Locator.locate(GlobalID.parse(params[:global_any_imagable]))
-
@images = Image.where(base_imagable: @imagable).or(Image.where(imagable: @imagable))
-
else
-
1
@imagable = find_resource
-
1
@images = @imagable.images
-
end
-
-
1
if @imagable
-
-
1
if params[:sort].present? && params[:order].present?
-
if params[:sort] == 'creator'
-
@images = @images.joins(:creator).reorder("CONCAT(users.first_name, ' ', users.last_name) #{params[:order]}")
-
else
-
@images = @images.reorder(params[:sort] => params[:order])
-
end
-
end
-
else
-
Rails.logger.debug "No images"
-
@images = Image.none
-
end
-
-
1
@images = @images.left_outer_joins(:image_classification)
-
-
1
respond_to do |format|
-
1
format.html # index.html.erb
-
1
format.json {
-
render :json => {
-
:total => @images.count,
-
:rows => @images.limit(params[:limit]).offset(params[:offset]).collect{ |u|
-
u.as_json.merge!({
-
classification: u.image_classification&.to_s,
-
link_image: view_context.link_to(view_context.image_tag(u.image.url(:thumb)), u.image.url, :class => "img-responsive gallery-image", :data => {:lightbox => "gallery"}, :title => u.original_filename),
-
imagable_to_s: u.imagable.to_s,
-
creator: u.creator.to_s
-
})
-
}
-
}
-
}
-
-
end
-
-
end
-
-
# GET /images/new
-
1
def new
-
1
@image = Image.new
-
1
@imagable = find_resource
-
-
1
puts @imagable.inspect
-
-
1
@form_view = params[:form_view]
-
-
end
-
-
# GET /images/1/edit
-
1
def edit
-
1
@imagable = @image.imagable
-
1
@form_view = params[:form_view]
-
end
-
-
1
def download
-
-
if @image.nil?
-
notify_user(:alert, 'Record not found!')
-
redirect_to( root_path )
-
return
-
end
-
# read the attachment
-
content = open(@image.image.url, "User-Agent" => "Ruby/#{RUBY_VERSION}") {|f| f.read}
-
# Send to the client
-
send_data content, :filename => @image.original_filename
-
-
end
-
-
# POST /images
-
# POST /images.json
-
1
def create
-
1
@form_view = params[:form_view]
-
-
1
@image = Image.new(form_params)
-
1
if @image.imagable.nil?
-
1
@imagable = find_resource
-
1
@image.imagable = @imagable
-
end
-
-
1
@image.base_imagable = @image.imagable if @image.base_imagable.nil?
-
-
1
@image.creator = current_user
-
-
1
respond_to do |format|
-
1
if @image.save
-
1
notify_user(:notice, 'Image was successfully created.')
-
2
format.html { redirect_back(fallback_location: root_path) }
-
1
format.json { render action: 'show', status: :created, location: @image }
-
else
-
format.html { render action: 'new' }
-
format.json { render json: @image.errors, status: :unprocessable_entity }
-
end
-
end
-
end
-
-
# PATCH/PUT /images/1
-
# PATCH/PUT /images/1.json
-
1
def update
-
1
@form_view = params[:form_view]
-
-
1
@imagable = @image.imagable
-
-
1
respond_to do |format|
-
1
if @image.update(form_params)
-
1
notify_user(:notice, 'Image was successfully updated.')
-
2
format.html { redirect_back(fallback_location: root_path) }
-
1
format.json { head :no_content }
-
else
-
format.html { render action: 'edit' }
-
format.json { render json: @image.errors, status: :unprocessable_entity }
-
end
-
end
-
end
-
-
# DELETE /images/1
-
# DELETE /images/1.json
-
1
def destroy
-
-
1
@imagable = @image.imagable
-
1
@image.destroy
-
-
1
notify_user(:notice, 'Image was successfully removed.')
-
1
respond_to do |format|
-
2
format.html { redirect_back(fallback_location: root_path) }
-
1
format.json { head :no_content }
-
end
-
end
-
-
1
private
-
-
# Use callbacks to share common setup or constraints between actions.
-
1
def set_image
-
3
@image = Image.find_by(:object_key => params[:id])
-
end
-
-
# Never trust parameters from the scary internet, only allow the white list through.
-
1
def form_params
-
2
params.require(:image).permit(Image.allowable_params)
-
end
-
-
end
-
1
class IssuesController < OrganizationAwareController
-
-
1
before_action :set_issue, only: [:show, :edit, :update, :destroy, :success, :review]
-
-
# Lock down the controller
-
1
authorize_resource only: [:index, :show, :new, :create, :edit, :update, :destroy]
-
-
1
add_breadcrumb "Home", :root_path
-
-
# GET /issues
-
1
def index
-
@issues = Issue.all
-
end
-
-
# GET /issues/1
-
1
def show
-
add_breadcrumb "Viewing Issue"
-
end
-
-
# GET /issues/1/review
-
1
def review
-
add_breadcrumb "Viewing Issue"
-
end
-
-
# GET /issues/1
-
1
def success
-
1
add_breadcrumb "Success"
-
end
-
-
# GET /issues/new
-
1
def new
-
1
add_breadcrumb "Report an issue"
-
-
1
@issue = Issue.new
-
end
-
-
# GET /issues/1/edit
-
1
def edit
-
add_breadcrumb "Resolve an issue"
-
-
if can? :manage_issue, @issue
-
@resolving_issue = true
-
else
-
redirect_to review_issue_path(@issue)
-
end
-
end
-
-
# POST /issues
-
1
def create
-
1
@issue = Issue.new(issue_params)
-
1
@issue.creator = current_user
-
-
1
add_breadcrumb "Report an issue"
-
-
1
if @issue.save
-
1
redirect_to success_issue_path(@issue)
-
else
-
render :new
-
end
-
end
-
-
# PATCH/PUT /issues/1
-
1
def update
-
if @issue.update(issue_params)
-
redirect_to success_issue_path(@issue)
-
else
-
@resolving_issue = true
-
render :edit
-
end
-
end
-
-
# DELETE /issues/1
-
1
def destroy
-
@issue.destroy
-
redirect_to issues_url, notice: 'Issue was successfully destroyed.'
-
end
-
-
1
private
-
# Use callbacks to share common setup or constraints between actions.
-
1
def set_issue
-
1
@issue = Issue.find_by_object_key(params[:id]) unless params[:id].nil?
-
1
if @issue.nil?
-
redirect_to '/404'
-
return
-
end
-
end
-
-
# Only allow a trusted parameter "white list" through.
-
1
def issue_params
-
1
params.require(:issue).permit(Issue.allowable_params)
-
end
-
end
-
1
class ManufacturersController < OrganizationAwareController
-
-
1
add_breadcrumb "Home", :root_path
-
-
# Lock down the controller
-
1
authorize_resource
-
-
1
def index
-
-
1
add_breadcrumb "Manufacturers", manufacturers_path
-
-
1
@manufacturers = Manufacturer.joins(:assets).where(assets: {organization_id: @organization_list}).distinct
-
-
1
respond_to do |format|
-
1
format.html # index.html.erb
-
1
format.json { render :json => @manufacturers }
-
end
-
end
-
-
1
protected
-
-
1
private
-
-
end
-
class MessageTemplatesController < OrganizationAwareController
-
-
add_breadcrumb "Home", :root_path
-
add_breadcrumb 'Client Admin Interface', :client_admin_path
-
add_breadcrumb "Messages", :message_templates_path
-
-
before_action :set_message_template, :only => [:edit, :update, :destroy]
-
-
# Lock down the controller
-
authorize_resource only: [:index, :edit, :update, :new, :create, :destroy]
-
-
# Session Variables
-
#INDEX_KEY_LIST_VAR = "message_templates_list_cache_var"
-
-
def index
-
# Get the templates
-
@message_templates = MessageTemplate.all
-
respond_to do |format|
-
format.html # index.html.erb
-
format.json { render :json => @message_templates }
-
end
-
end
-
-
def new
-
add_breadcrumb "New"
-
-
@message_template = MessageTemplate.new
-
-
respond_to do |format|
-
format.html
-
end
-
end
-
-
def create
-
@message_template = MessageTemplate.new(form_params)
-
-
respond_to do |format|
-
if @message_template.save
-
format.html { redirect_to message_templates_url }
-
else
-
format.html { render :action => "new" }
-
end
-
end
-
end
-
-
def edit
-
add_breadcrumb @message_template.name
-
-
respond_to do |format|
-
format.html
-
end
-
-
end
-
-
def update
-
respond_to do |format|
-
if @message_template.update_attributes(form_params)
-
format.html { redirect_to message_templates_url }
-
else
-
format.html { render :action => "edit" }
-
end
-
end
-
end
-
-
def destroy
-
@message_template.destroy
-
respond_to do |format|
-
notify_user(:notice, "Message Template #{@message_template.name} has been deleted.")
-
format.html { redirect_to message_templates_url }
-
format.json { head :no_content }
-
end
-
end
-
-
def message_history
-
@messages = Message.unscoped.left_outer_joins(:message_template).joins(:user).order(created_at: :desc).all
-
-
unless params[:search].blank?
-
search_string = ['message_templates.name', 'message_templates.description', "users.first_name", "users.last_name", 'messages.subject', 'email_status'].map{|r| "#{r} LIKE '%#{params[:search]}%'"}.join(' OR ')
-
-
# parse dates for searching
-
search_string << " OR (DATE(messages.created_at) BETWEEN '#{Chronic.parse(params[:search], guess: :begin)}' AND '#{Chronic.parse(params[:search], guess: :end)}')"
-
-
@messages = @messages.where(Arel.sql(search_string))
-
end
-
-
params[:sort] ||= 'created_at'
-
-
sorting_string = "#{params[:sort]} #{params[:order]}"
-
-
respond_to do |format|
-
format.html
-
format.js
-
format.json {
-
render :json => {
-
:total => @messages.count,
-
:rows => @messages.reorder(sorting_string).limit(params[:limit]).offset(params[:offset]).as_json
-
}
-
}
-
format.xls do
-
response.headers['Content-Disposition'] = "attachment; filename=message_history.xls"
-
end
-
format.xlsx do
-
response.headers['Content-Disposition'] = "attachment; filename=message_history.xlsx"
-
end
-
end
-
end
-
-
-
#------------------------------------------------------------------------------
-
#
-
# Private Methods
-
#
-
#------------------------------------------------------------------------------
-
private
-
-
# Never trust parameters from the scary internet, only allow the white list through.
-
def form_params
-
params.require(:message_template).permit(MessageTemplate.allowable_params)
-
end
-
-
# Callbacks to share common setup or constraints between actions.
-
def set_message_template
-
@message_template = MessageTemplate.find_by_object_key(params[:id]) unless params[:id].nil?
-
if @message_template.nil?
-
redirect_to '/404'
-
return
-
end
-
end
-
-
end
-
1
class MessagesController < OrganizationAwareController
-
-
1
add_breadcrumb "Home", :root_path
-
-
1
before_action :set_message, :only => [:show, :destroy, :tag, :reply]
-
1
before_action :check_for_cancel, :only => [:create]
-
-
# Lock down the controller
-
1
authorize_resource only: [:index, :show, :new, :create, :destroy]
-
-
# Session Variables
-
1
INDEX_KEY_LIST_VAR = "messages_list_cache_var"
-
-
1
NEW_MESSAGE_FILTER = '1'
-
1
FLAGGED_MESSAGE_FILTER = '2'
-
1
ALL_MESSAGE_FILTER = '3'
-
1
SENT_MESSAGE_FILTER = '4'
-
-
1
def index
-
-
1
add_breadcrumb "My Messages", user_messages_path(current_user)
-
-
# Start to set up the query
-
1
conditions = []
-
1
values = []
-
-
# New messages must be for the current user
-
1
conditions << 'to_user_id = ?'
-
1
values << current_user.id
-
-
# Get the messages
-
1
@messages = Message.where(conditions.join(' AND '), *values).order("created_at DESC")
-
1
@all_messages = @messages.where('opened_at IS NOT NULL')
-
1
@new_messages = @messages.where('opened_at IS NULL')
-
1
@flagged_messages = current_user.messages
-
1
@sent_messages = Message.unscoped.where(:user_id => current_user.id).order("created_at DESC")
-
-
# cache the set of asset ids in case we need them later
-
1
cache_list(@all_messages, "#{ALL_MESSAGE_FILTER}_#{INDEX_KEY_LIST_VAR}")
-
1
cache_list(@new_messages, "#{NEW_MESSAGE_FILTER}_#{INDEX_KEY_LIST_VAR}")
-
1
cache_list(@flagged_messages, "#{FLAGGED_MESSAGE_FILTER}_#{INDEX_KEY_LIST_VAR}")
-
1
cache_list(@sent_messages, "#{SENT_MESSAGE_FILTER}_#{INDEX_KEY_LIST_VAR}")
-
-
1
respond_to do |format|
-
1
format.html # index.html.erb
-
1
format.json { render :json => @messages }
-
end
-
end
-
-
# Tags the message for the user or removes it if the message
-
# is already tagged. called by ajax so no response is rendered
-
1
def tag
-
-
2
if @message.tagged? current_user
-
1
@message.users.delete current_user
-
else
-
1
@message.tag current_user
-
end
-
-
# No response needed
-
2
render body: current_user
-
-
end
-
-
1
def show
-
-
1
add_breadcrumb "My Messages", user_messages_path(current_user)
-
1
add_breadcrumb @message.subject, user_message_path(current_user, @message)
-
-
1
@response = Message.new
-
1
@response.organization = @organization
-
1
@response.user = current_user
-
1
@response.priority_type = @message.priority_type
-
-
# Mark this message as opened if not opened previously as long as the current_user
-
# is the message recipient
-
1
if @message.opened_at.nil? and current_user == @message.to_user
-
1
@message.opened_at = Time.current
-
1
@message.save
-
end
-
-
# get the @prev_record_path and @next_record_path view vars
-
1
get_next_and_prev_object_keys(@message, "#{params[:filter]}_#{INDEX_KEY_LIST_VAR}")
-
1
@prev_record_path = @prev_record_key.nil? ? "#" : user_message_path(current_user, @prev_record_key, :filter => params[:filter])
-
1
@next_record_path = @next_record_key.nil? ? "#" : user_message_path(current_user, @next_record_key, :filter => params[:filter])
-
-
1
respond_to do |format|
-
1
format.js # show.html.erb
-
1
format.html # show.html.erb
-
1
format.json { render :json => @message }
-
end
-
end
-
-
1
def new
-
-
1
add_breadcrumb "My Messages", user_messages_path(current_user)
-
1
add_breadcrumb "New"
-
-
1
@message_proxy = MessageProxy.new
-
-
1
@message_proxy.priority_type_id = PriorityType.default.id
-
1
@message_proxy.to_user_ids << User.find_by_object_key(params[:to_user]).id unless params[:to_user].nil?
-
1
@message_proxy.available_agencies = (@organization_list + current_user.organization_ids).uniq
-
1
@message_proxy.subject = params[:subject] unless params[:subject].nil?
-
1
@message_proxy.body = params[:body] unless params[:body].nil?
-
-
1
respond_to do |format|
-
1
format.html # new.html.haml this had been an erb and is now an haml the change should just be caught
-
end
-
end
-
-
1
def create
-
-
add_breadcrumb "My Messages", user_messages_path(current_user)
-
add_breadcrumb "New"
-
-
@message_proxy = MessageProxy.new(message_proxy_form_params)
-
Rails.logger.debug @message_proxy.inspect
-
-
respond_to do |format|
-
if @message_proxy.valid?
-
priority = PriorityType.find(@message_proxy.priority_type_id)
-
send_count = 0
-
-
if @message_proxy.send_to_group == '1'
-
org_list = []
-
@message_proxy.group_agencys.uniq.each {|x| org_list << x unless x.blank?}
-
# Sending to a group
-
selected_users = User.where(:active => true)
-
# Select agencies if provided
-
selected_users = selected_users.where(:organization_id => org_list) unless org_list.empty?
-
# Select roles if provided
-
@message_proxy.group_roles.each do |role_id|
-
selected_users = selected_users.with_role(Role.find(role_id).name) unless role_id.blank?
-
end
-
selected_users.each do |user|
-
msg = Message.new
-
msg.user = current_user
-
msg.organization = @organization
-
msg.to_user = user
-
msg.subject = @message_proxy.subject
-
msg.body = @message_proxy.body
-
msg.priority_type = priority
-
msg.save
-
send_count += 1
-
end
-
else
-
# Sending to individual users
-
@message_proxy.to_user_ids.each do |user_id|
-
unless user_id.blank?
-
msg = Message.new
-
msg.user = current_user
-
msg.organization = @organization
-
msg.to_user = User.find(user_id)
-
msg.subject = @message_proxy.subject
-
msg.body = @message_proxy.body
-
msg.priority_type = priority
-
msg.save
-
send_count += 1
-
end
-
end
-
end
-
notify_user(:notice, "#{view_context.pluralize( send_count, 'Messages')} successfully sent.")
-
format.html { redirect_to user_messages_url(current_user) }
-
else
-
Rails.logger.debug @message_proxy.errors
-
@message_proxy.available_agencies = (@organization_list + current_user.organization_ids).uniq
-
format.html { render :action => "new" }
-
end
-
end
-
end
-
-
# The user has posted a reply to an existing message
-
1
def reply
-
-
# old message is set from the filter
-
1
@new_message = Message.new(form_params)
-
1
@new_message.organization = @organization
-
1
@new_message.user = current_user
-
1
@new_message.priority_type = @message.priority_type
-
1
@new_message.subject = 'Re: ' + @message.subject
-
1
@new_message.to_user = @message.user
-
-
1
respond_to do |format|
-
1
if @new_message.save
-
1
notify_user(:notice, "Reply was successfully sent.")
-
-
1
@message.responses << @new_message
-
-
2
format.html { redirect_to user_messages_url(current_user) }
-
1
format.json { render :json => @new_message, :status => :created }
-
else
-
@response = @new_message
-
format.html { render :action => "show" }
-
format.json { render :json => @new_message.errors, :status => :unprocessable_entity }
-
end
-
end
-
end
-
-
1
def destroy
-
@message.active = false
-
@message.save(:validate => false)
-
respond_to do |format|
-
notify_user(:notice, "Message has been deleted.")
-
format.html { redirect_to user_messages_url(current_user) }
-
format.json { head :no_content }
-
end
-
end
-
-
-
#------------------------------------------------------------------------------
-
#
-
# Private Methods
-
#
-
#------------------------------------------------------------------------------
-
1
private
-
-
1
def check_for_cancel
-
unless params[:cancel].blank?
-
# check that the user has access to this agency
-
redirect_to(user_messages_url(current_user))
-
return
-
end
-
end
-
-
# Never trust parameters from the scary internet, only allow the white list through.
-
1
def form_params
-
1
params.require(:message).permit(Message.allowable_params)
-
end
-
-
1
def message_proxy_form_params
-
params.require(:message_proxy).permit(MessageProxy.allowable_params)
-
end
-
-
# Callbacks to share common setup or constraints between actions.
-
1
def set_message
-
4
@message = Message.unscoped.find_by_object_key(params[:id]) unless params[:id].nil?
-
4
if @message.nil?
-
redirect_to '/404'
-
return
-
end
-
end
-
-
end
-
#
-
# Use this class as the base class for controllers which deal with polymorphic classes
-
# such as comments (commentable), documents (documetable), etc.
-
#
-
1
class NestedResourceController < OrganizationAwareController
-
-
1
protected
-
-
# Returns the URL of the resource that is being commented. There is a special case where
-
# controllers which are aliased (eg asset => inventory) get the wrong controller name
-
# so we have to deal with those
-
1
def get_resource_url(resource)
-
#puts resource.inspect
-
2
controller_name = resource.class.name.underscore
-
#puts controller_name
-
2
if controller_name == Rails.application.config.asset_base_class_name.underscore
-
2
controller_name = 'inventory'
-
end
-
2
eval("#{controller_name}_url('#{resource.object_key}')")
-
end
-
-
# Get the class and object key of the commentable object we are operating on. There is a special
-
# case where the controller is aliased and we need to determine this and replace the correct
-
# resource class name
-
1
def find_resource
-
12
params.permit!.to_h.reverse_each do |name, value|
-
36
if name =~ /(.+)_id$/
-
11
if $1 == 'inventory'
-
6
return Rails.application.config.asset_base_class_name.constantize.find_by_object_key(value)
-
else
-
5
return $1.classify.constantize.find_by_object_key(value)
-
end
-
end
-
end
-
-
nil
-
end
-
-
end
-
1
class NoticesController < OrganizationAwareController
-
-
1
add_breadcrumb "Home", :root_path
-
1
add_breadcrumb "Notices", :notices_path
-
-
1
before_action :get_notice, :except => [:index, :create, :new]
-
-
# Protect controller methods using the cancan ability
-
1
authorize_resource
-
-
# Session Variables
-
1
INDEX_KEY_LIST_VAR = "notice_list_cache_var"
-
-
1
def index
-
-
3
if current_user.has_role? :admin
-
1
@notices = Notice.all.order(:display_datetime)
-
else
-
2
@notices = Notice.visible.order(:display_datetime)
-
end
-
# cache the set of notices ids in case we need them later
-
3
cache_list(@notices, INDEX_KEY_LIST_VAR)
-
-
3
respond_to do |format|
-
3
format.html # index.html.erb
-
3
format.json { render :json => @notices }
-
end
-
end
-
-
1
def show
-
1
add_breadcrumb ActionController::Base.helpers.sanitize(@notice.subject)
-
-
# get the @prev_record_path and @next_record_path view vars
-
1
get_next_and_prev_object_keys(@notice, INDEX_KEY_LIST_VAR)
-
1
@prev_record_path = @prev_record_key.nil? ? "#" : notice_path(@prev_record_key)
-
1
@next_record_path = @next_record_key.nil? ? "#" : notice_path(@next_record_key)
-
-
end
-
-
1
def new
-
-
1
Rails.logger.debug "In notices#new"
-
1
add_breadcrumb "New"
-
1
@notice = Notice.new
-
-
end
-
-
# @notice set in before_action
-
1
def reactivate
-
1
add_breadcrumb @notice, notice_path(@notice)
-
1
add_breadcrumb "Reactivate"
-
-
# shift dates forward but maintain duration. Set active
-
1
duration = @notice.duration_in_hours
-
1
@notice.display_datetime = DateTime.current.beginning_of_hour
-
1
@notice.end_datetime = @notice.display_datetime.advance(:hours => duration)
-
1
@notice.active = true
-
1
@notice.save
-
-
1
render "new"
-
end
-
-
1
def edit
-
-
1
add_breadcrumb @notice, notice_path(@notice)
-
1
add_breadcrumb "Update"
-
-
end
-
-
1
def create
-
-
1
add_breadcrumb "New"
-
1
@notice = Notice.new(form_params)
-
-
1
respond_to do |format|
-
1
if @notice.save
-
1
notify_user(:notice, "Notice was successfully created.")
-
2
format.html { redirect_to notices_url }
-
1
format.json { render :json => @notice, :status => :created, :location => @notice }
-
else
-
format.html { render :action => "new" }
-
format.json { render :json => @notice.errors, :status => :unprocessable_entity }
-
end
-
end
-
end
-
-
1
def update
-
-
1
add_breadcrumb @notice, notice_path(@notice)
-
1
add_breadcrumb "Notice"
-
-
1
respond_to do |format|
-
1
if @notice.update_attributes(form_params)
-
1
notify_user(:notice, "Notice was successfully updated.")
-
2
format.html { redirect_to notices_url }
-
1
format.json { head :no_content }
-
else
-
format.html { render :action => "edit" }
-
format.json { render :json => @notice.errors, :status => :unprocessable_entity }
-
end
-
end
-
end
-
-
1
def deactivate
-
-
1
@notice.active = false
-
1
@notice.save
-
-
1
redirect_back(fallback_location: root_path)
-
-
end
-
-
1
def destroy
-
-
1
@notice.destroy
-
1
notify_user(:notice, "Notice was successfully removed.")
-
1
respond_to do |format|
-
2
format.html { redirect_to notices_url }
-
1
format.json { head :no_content }
-
end
-
end
-
-
1
private
-
# Never trust parameters from the scary internet, only allow the white list through.
-
1
def form_params
-
2
params.require(:notice).permit(Notice.allowable_params)
-
end
-
-
1
def get_notice
-
# See if it is our policy
-
6
@notice = Notice.find_by_object_key(params[:id]) unless params[:id].nil?
-
# if not found or the object does not belong to the users
-
# send them back to index.html.erb
-
6
if @notice.nil?
-
redirect_to '/404'
-
return
-
end
-
-
end
-
-
end
-
1
class NotificationsController < OrganizationAwareController
-
-
1
prepend_before_action :skip_timeout, :only => [:count]
-
-
1
before_action :set_notification, :only => [:show, :update]
-
-
# Lock down the controller
-
1
authorize_resource only: [:index, :show, :update]
-
-
# always get all notifications only for the current user logged in
-
1
def index
-
@notifications = current_user ? current_user.user_notifications.unopened : []
-
-
respond_to do |format|
-
format.js { render :partial => "shared/notifications_list", locals: { :notifications => @notifications } }
-
end
-
-
end
-
-
1
def count
-
request.env["devise.skip_trackable"] = true
-
-
@count = current_user ? current_user.user_notifications.unopened.count : 0
-
-
respond_to do |format|
-
format.json { render :json => @count.to_json }
-
end
-
end
-
-
1
def read_all
-
current_user.user_notifications.unopened.update_all(opened_at: Time.now) if current_user
-
-
respond_to do |format|
-
format.js { render text: 'countNotifications(); $("#notificationContainer").hide();' } # run JS function on main notification nav to recount notifications
-
end
-
end
-
-
1
def show
-
-
# reset filter if necessary so you go to notification link
-
if @notification.notifiable_type == 'Organization' && !(@organization_list.include? @notification.notifiable_id)
-
set_current_user_organization_filter_(current_user, current_user.user_organization_filters.system_filters.first)
-
end
-
-
# if all users of that notification have seen it make notification inactive
-
if @notification.user_notifications.count == @notification.user_notifications.opened.count
-
@notification.update(active: false)
-
end
-
-
redirect_to @notification.link
-
end
-
-
1
def update
-
-
respond_to do |format|
-
format.js { render text: 'countNotifications(); getNotifications();' } # run JS function on main notification nav to recount notifications
-
end
-
end
-
-
1
private
-
-
1
def set_notification
-
@notification = Notification.find_by_object_key(params[:id]) unless params[:id].nil?
-
if @notification.nil?
-
redirect_to '/404'
-
return
-
else
-
# update the noification as opend at by the logged in user
-
@notification.user_notifications.find_by(user: current_user).update!(opened_at: Time.now)
-
end
-
end
-
-
1
def skip_timeout
-
request.env["devise.skip_trackable"] = true
-
end
-
-
end
-
1
class OrganizationsController < OrganizationAwareController
-
-
1
add_breadcrumb "Home", :root_path
-
1
add_breadcrumb "Organizations", :organizations_path
-
-
1
before_action :get_org, :only => [:show, :map, :edit, :update]
-
-
# Lock down the controller
-
1
authorize_resource except: [:get_policies]
-
-
# include the transam markers mixin
-
1
include TransamMapMarkers
-
-
1
INDEX_KEY_LIST_VAR = "organization_key_list_cache_var"
-
1
SESSION_VIEW_TYPE_VAR = 'organization_subnav_view_type'
-
-
1
def get_policies
-
org_id = params[:organization_id]
-
-
result = Policy.where(organization_id: org_id).order(active: :desc).pluck(:id, :description, :active).map{|p| [p[0], "#{p[1]} #{p[2] ? '(Current)' : ''}"]}
-
-
respond_to do |format|
-
format.json { render json: result.to_json }
-
end
-
end
-
-
# GET /asset
-
# GET /asset.json
-
1
def index
-
# Start to set up the query
-
1
conditions = []
-
1
values = []
-
-
1
@organization_type_id = params[:organization_type_id]
-
1
unless @organization_type_id.blank?
-
@organization_type_id = @organization_type_id.to_i
-
conditions << 'organization_type_id = ?'
-
values << @organization_type_id
-
-
add_breadcrumb OrganizationType.find(@organization_type_id).name.pluralize(2), organizations_path(:organization_type_id => @organization_type_id)
-
end
-
-
1
if params[:show_active_only].nil?
-
1
@show_active_only = 'active'
-
else
-
@show_active_only = params[:show_active_only]
-
end
-
-
1
if @show_active_only == 'active'
-
1
conditions << 'organizations.active = ?'
-
1
values << true
-
elsif @show_active_only == 'inactive'
-
conditions << 'organizations.active = ?'
-
values << false
-
end
-
-
1
conditions << 'id IN (?)'
-
1
values << @organization_list
-
-
1
@organizations = Organization.where(conditions.join(' AND '), *values)
-
-
# cache the set of asset ids in case we need them later
-
1
cache_list(@organizations, INDEX_KEY_LIST_VAR)
-
-
# remember the view type
-
1
@view_type = get_view_type(SESSION_VIEW_TYPE_VAR)
-
-
# If we are building a map, make sure we get the map artifacts
-
1
if @view_type == VIEW_TYPE_MAP
-
@markers = generate_map_markers(@organizations)
-
end
-
1
respond_to do |format|
-
1
format.html # index.html.erb
-
1
format.json { render :json => @organizations }
-
end
-
end
-
-
1
def show
-
-
2
if cannot? :read, @org
-
redirect_to '/403'
-
return
-
end
-
-
2
add_breadcrumb @org.organization_type.name.pluralize(2), organizations_path(:organization_type_id => @org.organization_type.id)
-
2
add_breadcrumb @org.short_name
-
-
# get the @prev_record_path and @next_record_path view vars
-
2
get_next_and_prev_object_keys(@org, INDEX_KEY_LIST_VAR)
-
2
@prev_record_path = @prev_record_key.nil? ? "#" : organization_path(@prev_record_key)
-
2
@next_record_path = @next_record_key.nil? ? "#" : organization_path(@next_record_key)
-
-
2
@organizations = []
-
2
@organizations << @org
-
2
@markers = generate_map_markers(@organizations, true)
-
-
# get the data for the tabs
-
2
@users = @org.users.active
-
-
2
if @org.try(:assets)
-
2
rep = AssetSubtypeReport.new
-
2
@asset_data = rep.get_data_from_collection(@org.assets)
-
2
@total_assets = @org.assets.count
-
end
-
-
2
respond_to do |format|
-
2
format.html # show.html.erb
-
2
format.json { render :json => @org }
-
end
-
end
-
-
1
def new
-
-
add_breadcrumb 'New', new_organization_path
-
-
# TODO expand to allow creation of any/most org types
-
-
org_type = OrganizationType.find_by(id: params[:organization_type_id])
-
-
if org_type
-
@org = org_type.class_name.constantize.new
-
else
-
redirect_to organizations_path, notice: 'Organization cannot be created without an organization type.'
-
end
-
end
-
-
1
def create
-
-
org_type = OrganizationType.find_by(id: params[:organization][:organization_type_id])
-
if org_type
-
bootstrap_toggle_fields = organization_allowable_params.select{|x| (x.to_s.include? '_ids') && !(x.to_s.include? '[]')}
-
@org = org_type.class_name.constantize.new(form_params.except(*bootstrap_toggle_fields))
-
-
if @org.save
-
-
bootstrap_toggle_fields.each do |field|
-
if form_params[field].present?
-
list = form_params[field].split(',')
-
@org.send("#{field.to_s}=",list)
-
end
-
end
-
-
@org.updates_after_create
-
-
# set organization role mappings
-
org_type.role_mappings.each do |role|
-
OrganizationRoleMapping.create(organization_id: @org.id, role_id: role.id)
-
end
-
-
# set current session to current org filter again in case new org in current filter
-
set_current_user_organization_filter_(current_user, current_user.user_organization_filter)
-
get_organization_selections
-
-
redirect_to organization_path(@org), notice: 'Organization was successfully created.'
-
else
-
render :new
-
end
-
end
-
end
-
-
# Edit simply returns the selected organization
-
1
def edit
-
1
if @org.nil?
-
notify_user(:alert, "Record not found.")
-
redirect_to organizations_url
-
return
-
end
-
-
1
add_breadcrumb @org.organization_type.name.pluralize(2), organizations_path(:organization_type_id => @org.organization_type.id)
-
1
add_breadcrumb @org.short_name, organization_path(@org)
-
1
add_breadcrumb "Update"
-
-
1
@page_title = "Update: #{@org.name}"
-
end
-
-
1
def update
-
-
1
if @org.nil?
-
notify_user(:alert, "Record not found.")
-
redirect_to organizations_url
-
return
-
end
-
-
1
add_breadcrumb @org.organization_type.name.pluralize(2), organizations_path(:organization_type_id => @org.organization_type.id)
-
1
add_breadcrumb @org.short_name, organization_path(@org)
-
1
add_breadcrumb "Update"
-
-
1
respond_to do |format|
-
19
bootstrap_toggle_fields = organization_allowable_params.select{|x| (x.to_s.include? '_ids') && !(x.to_s.include? '[]')}
-
1
if @org.update_attributes(form_params.except(*bootstrap_toggle_fields))
-
1
bootstrap_toggle_fields.each do |field|
-
if form_params[field].present?
-
list = form_params[field].split(',')
-
@org.send("#{field.to_s}=",list)
-
end
-
end
-
1
notify_user(:notice, "#{@org.name} was successfully updated.")
-
2
format.html { redirect_to organization_url(@org) }
-
1
format.json { head :no_content }
-
else
-
format.html { render :action => "edit" }
-
format.json { render :json => @org.errors, :status => :unprocessable_entity }
-
end
-
end
-
end
-
-
#------------------------------------------------------------------------------
-
#
-
# Protected Methods
-
#
-
#------------------------------------------------------------------------------
-
1
protected
-
#
-
# generate an array of map markers for use with the leaflet plugin
-
#
-
1
def generate_map_markers(organizations_array, render_open = false)
-
2
objs = []
-
2
organizations_array.each do |org|
-
2
objs << get_organization_marker(org, render_open) unless org.latitude.nil?
-
end
-
2
return objs.to_json
-
end
-
-
#------------------------------------------------------------------------------
-
#
-
# Private Methods
-
#
-
#------------------------------------------------------------------------------
-
1
private
-
-
# Returns the agency that has been selected by the user. The agency must
-
# be user's agency or one of its member agencies.
-
1
def get_org
-
5
if params[:id].nil?
-
org = current_user.organization
-
else
-
5
org = Organization.find_by(:short_name => params[:id])
-
end
-
5
if org.present?
-
4
@org = get_typed_organization(org)
-
else
-
1
redirect_to '/404'
-
return
-
end
-
end
-
-
# Never trust parameters from the scary internet, only allow the white list through.
-
1
def form_params
-
1
params.require(:organization).permit(organization_allowable_params)
-
end
-
-
end
-
class PagesController < OrganizationAwareController
-
include HighVoltage::StaticPage
-
-
end
-
1
class PasswordsController < Devise::PasswordsController
-
-
# determine which layout to use based on the current user state
-
1
layout :layout_by_resource
-
-
# Determine which layout to use based on the authorized state
-
1
def layout_by_resource
-
2
if user_signed_in?
-
1
"application"
-
else
-
1
"password"
-
end
-
end
-
-
end
-
class PoliciesController < OrganizationAwareController
-
-
add_breadcrumb "Home", :root_path
-
add_breadcrumb "Policies", :rule_sets_path
-
-
skip_before_action :get_organization_selections
-
before_action :set_viewable_organizations
-
-
#before_action :authorize_admin
-
before_action :get_policy, :except => [:index, :create, :new]
-
-
# Lock down the controller
-
authorize_resource only: [:index, :show, :create, :edit, :update, :destroy]
-
-
-
SESSION_VIEW_TYPE_VAR = 'policies_subnav_view_type'
-
-
def get_subtype_minimum_value
-
-
valid = true
-
-
if @policy.parent.present?
-
rule = @policy.parent.policy_asset_subtype_rules.find_by(asset_subtype_id: params[:asset_subtype_id].to_i)
-
-
min = 0
-
if rule.present?
-
params[:policy_asset_subtype_rule].each do |key, val|
-
min = rule.send(key)
-
valid = (min <= val.to_i)
-
end
-
end
-
end
-
-
results = (valid ? valid : "Please enter a value greater than or equal to #{min}.").to_json
-
respond_to do |format|
-
format.json { render :json => results}
-
end
-
end
-
-
def check_subtype_rule_exists
-
valid = @policy.policy_asset_subtype_rules.find_by(asset_subtype_id: params[:asset_subtype_id].to_i, fuel_type_id: params[:fuel_type_id]).present?
-
-
respond_to do |format|
-
format.json { render :json => valid}
-
end
-
end
-
-
def index
-
if params[:organization_id]
-
org_id = params[:organization_id]
-
else
-
org_id = @organization_list.first
-
end
-
-
if params[:policy_id]
-
policy = Policy.find_by(id: params[:policy_id], organization_id: org_id)
-
else
-
policy = Policy.active.find_by(organization_id: org_id)
-
end
-
-
redirect_to policy_path(policy)
-
end
-
-
def show
-
-
add_breadcrumb "Asset Replacement/Rehabilitation Policy", policies_path
-
add_breadcrumb @policy.name, policy_path(@policy)
-
-
end
-
-
def new
-
#right now, no blank policies can be created - just copying existing policies
-
end
-
-
#-----------------------------------------------------------------------------
-
# Loads an edit form for a policy. called via ajax
-
#-----------------------------------------------------------------------------
-
def show_edit_form
-
-
@notice = params[:notice] if params[:notice]
-
-
@type = params[:type]
-
if @type == 'asset_type'
-
@rule = @policy.policy_asset_type_rules.find(params[:rule])
-
elsif @type == 'asset_subtype'
-
@rule = @policy.policy_asset_subtype_rules.find(params[:rule])
-
@asset_type = @rule.asset_subtype.asset_type
-
# Check to see if the user wants to create a copy
-
if params[:copy].to_i == 1
-
@copy = @rule.id
-
rule = @rule.dup
-
#rule.save
-
@rule = rule
-
-
end
-
-
if @copy || !(@rule.subtype_fuel_type_rule_used? (@policy.parent.present? ? [@policy.organization_id] : ([@policy.organization_id] + Policy.where(parent_id: @policy.id).pluck(:organization_id))))
-
@valid_fuel_types = FuelType.active.where.not(id: @policy.policy_asset_subtype_rules.where('id != ? AND asset_subtype_id = ?', @rule.id, @rule.asset_subtype_id).pluck(:fuel_type_id))
-
-
end
-
-
end
-
-
end
-
-
#-----------------------------------------------------------------------------
-
# Updates a policy rule for the current policy. Called via ajax
-
#-----------------------------------------------------------------------------
-
def update_policy_rule
-
-
if params[:policy_asset_type_rule].present?
-
rule = PolicyAssetTypeRule.find(params[:policy_asset_type_rule][:id])
-
rule.update_attributes(asset_type_rule_form_params)
-
else
-
rule = PolicyAssetSubtypeRule.find(params[:policy_asset_subtype_rule][:id])
-
rule.update_attributes(asset_subtype_rule_form_params)
-
end
-
-
try_create_replace_with_rule rule
-
-
end
-
-
#-----------------------------------------------------------------------------
-
# Removes a policy rule from the current policy. Called via ajax
-
#-----------------------------------------------------------------------------
-
def remove_policy_rule
-
-
rule = @policy.policy_asset_subtype_rules.find(params[:rule])
-
if rule.nil?
-
notify_user_immediately "Can't find the rule in policy #{@policy}", "warning"
-
elsif Asset.where(organization_id: @policy.organization_id, asset_subtype_id: rule.asset_subtype_id, fuel_type_id: rule.fuel_type_id).count > 0
-
notify_user_immediately "Can't remove a default rule being used from #{@policy}", "warning"
-
else
-
if rule.try(:parent_rule_can_destroy?)
-
AssetSubtype.find_by(id: rule.asset_subtype_id).destroy
-
end
-
rule.destroy
-
-
notify_user_immediately "Rule was successfully removed from #{@policy}"
-
end
-
-
render 'update_policy_rules'
-
-
end
-
-
#-----------------------------------------------------------------------------
-
# Adds a policy rule to the current policy. Called via ajax
-
#-----------------------------------------------------------------------------
-
def add_policy_rule
-
-
if params[:policy_asset_type_rule].present?
-
rule = PolicyAssetTypeRule.new(asset_type_rule_form_params)
-
-
if rule.asset_type.nil?
-
# for now default most fields
-
new_type = AssetType.create(
-
name: params[:new_asset_type_name],
-
description: params[:new_asset_type_description],
-
class_name: 'Component',
-
display_icon_name: 'fa fa-cogs',
-
map_icon_name: 'blueIcon',
-
active: true
-
)
-
rule.asset_type = new_type
-
end
-
else
-
if params[:copied_rule].present? # if the rule is copied, copy from old rule and overwrite with form
-
rule = PolicyAssetSubtypeRule.find(params[:copied_rule]).dup
-
rule.assign_attributes(asset_subtype_rule_form_params)
-
else
-
rule = PolicyAssetSubtypeRule.new(asset_subtype_rule_form_params)
-
end
-
-
# if creating a new asset subtype and policy rule
-
if rule.asset_subtype.nil?
-
new_subtype = AssetSubtype.create(
-
asset_type_id: params[:new_asset_subtype_asset_type_id],
-
name: params[:new_asset_subtype_name],
-
description: params[:new_asset_subtype_description],
-
active: true
-
)
-
rule.asset_subtype = new_subtype
-
end
-
end
-
-
-
-
if rule.present?
-
rule.policy = @policy
-
rule.save
-
end
-
-
try_create_replace_with_rule rule
-
-
-
-
end
-
-
#-----------------------------------------------------------------------------
-
# Shows the form for new policy rules. Called via ajax
-
#-----------------------------------------------------------------------------
-
def new_policy_rule
-
-
@valid_types = []
-
if params[:type] == '1'
-
@rule = PolicyAssetTypeRule.new(:policy => @policy)
-
@rule_type = 'asset_type'
-
AssetType.active.each do |at|
-
@valid_types << at unless @policy.asset_type_rule? at
-
end
-
else
-
@asset_type = AssetType.find(params[:asset_type])
-
@rule = PolicyAssetSubtypeRule.new(:policy => @policy)
-
@rule_type = 'asset_subtype'
-
AssetSubtype.active.where(:asset_type_id => @asset_type).each do |at|
-
@valid_types << at unless @policy.asset_subtype_rule? at
-
end
-
-
@valid_fuel_types = FuelType.active
-
end
-
render 'new_rule'
-
end
-
-
# Sets the current policy for an organization
-
def make_current
-
-
# Get the concrete class for the organization
-
org = Organization.get_typed_organization(@policy.organization)
-
-
# Make sure that any other policies including the selected ones
-
# are not current
-
org.policies.each do |pol|
-
pol.active = false
-
pol.save
-
end
-
# make the selected policy current
-
@policy.active = true
-
@policy.save
-
-
notify_user(:notice, "Policy #{@policy.name} is now set as the current policy.")
-
redirect_to policy_url(@policy)
-
-
end
-
-
def edit
-
-
add_breadcrumb "Asset Replacement/Rehabilitation Policy", policies_path
-
add_breadcrumb @policy.name, policy_path(@policy)
-
-
if params[:copy].to_i == 1
-
add_breadcrumb 'Copy', edit_policy_path(@policy, :copy => '1')
-
-
@copy = @policy.object_key
-
new_policy = @policy.dup
-
new_policy.object_key = nil
-
new_policy.parent = @policy.parent
-
new_policy.organization = @organization
-
new_policy.description = "Copy of " + @policy.description
-
new_policy.active = false
-
@policy = new_policy #set policy to newly copied policy
-
-
else
-
add_breadcrumb 'Modify', edit_policy_path(@policy)
-
end
-
-
end
-
-
# Copy a policy to a new policy. This could be copying from a parent agency to a member agency or
-
# vis versa
-
def copy
-
-
old_policy_name = @policy.name
-
new_policy = @policy.dup
-
new_policy.object_key = nil
-
new_policy.parent = @policy.parent
-
new_policy.organization = @organization
-
new_policy.active = false
-
-
new_policy.assign_attributes(form_params)
-
-
new_policy.save
-
# Copy all the records
-
@policy.policy_asset_type_rules.each do |r|
-
rule = r.dup
-
rule.policy = new_policy
-
rule.save
-
end
-
@policy.policy_asset_subtype_rules.each do |r|
-
rule = r.dup
-
rule.policy = new_policy
-
rule.save
-
end
-
-
# now attempt to load the newly created record
-
@policy = Policy.find(new_policy.id)
-
respond_to do |format|
-
if @policy
-
notify_user(:notice, "Policy #{old_policy_name} was successfully copied.")
-
format.html { redirect_to policy_url(@policy) }
-
format.json { render :json => @policy, :status => :created, :location => @policy }
-
else
-
format.html { render :action => "edit" }
-
format.json { render :json => @policy.errors, :status => :unprocessable_entity }
-
end
-
end
-
end
-
-
def create
-
@policy = Policy.new(form_params)
-
@policy.organization = @organization
-
-
respond_to do |format|
-
if @policy.save
-
notify_user(:notice, "Policy #{@policy.name} was successfully created.")
-
format.html { redirect_to policy_url(@policy) }
-
format.json { render :json => @policy, :status => :created, :location => @policy }
-
else
-
format.html { render :action => "new" }
-
format.json { render :json => @policy.errors, :status => :unprocessable_entity }
-
end
-
end
-
end
-
-
def update
-
-
add_breadcrumb "Asset Replacement/Rehabilitation Policy", policies_path
-
add_breadcrumb @policy.name, policy_path(@policy)
-
add_breadcrumb 'Modify', edit_policy_path(@policy)
-
-
respond_to do |format|
-
if @policy.update_attributes(form_params)
-
notify_user(:notice, "Policy #{@policy.name} was successfully updated.")
-
format.html { redirect_to policy_url(@policy) }
-
format.json { head :no_content }
-
else
-
format.html { render :action => "edit" }
-
format.json { render :json => @policy.errors, :status => :unprocessable_entity }
-
end
-
end
-
end
-
-
def destroy
-
-
@policy.destroy
-
notify_user(:notice, "Policy was successfully removed.")
-
respond_to do |format|
-
format.html { redirect_to policies_url }
-
format.json { head :no_content }
-
end
-
end
-
-
def runner
-
add_breadcrumb "Asset Replacement/Rehabilitation Policy", policies_path
-
add_breadcrumb @policy.name, policy_path(@policy)
-
add_breadcrumb "Policy Runner", runner_policy_path(@policy)
-
-
@builder_proxy = AssetUpdaterProxy.new(:policy => @policy)
-
@asset_types = AssetType.active.where(id: @policy.organization.asset_type_counts.keys)
-
@message = "Applying policy to selected assets. This may take a while..."
-
end
-
-
def update_assets
-
-
@builder_proxy = AssetUpdaterProxy.new(params[:asset_updater_proxy])
-
if @builder_proxy.valid?
-
-
Delayed::Job.enqueue AssetUpdateAllJob.new(@policy.organization, @builder_proxy.asset_types, current_user), :priority => 0
-
-
# Let the user know the results
-
msg = "Assets are being updated using policy #{@policy}. You will be notified when the process is complete."
-
notify_user(:notice, msg)
-
-
redirect_to policy_path @policy
-
return
-
else
-
respond_to do |format|
-
format.html { render :action => "updater" }
-
end
-
end
-
-
end
-
-
def inherit
-
add_breadcrumb "Asset Replacement/Rehabilitation Policy", policies_path
-
add_breadcrumb @policy.name, policy_path(@policy)
-
add_breadcrumb "Distribute Policy", inherit_policy_path(@policy)
-
-
@distributer_proxy = PolicyDistributerProxy.new(:policy => @policy)
-
@message = "Distributing and applying policies. This may take a while..."
-
end
-
-
def distribute
-
@distributer_proxy = PolicyDistributerProxy.new(params[:policy_distributer_proxy])
-
@distributer_proxy.policy = @policy
-
-
policies = Policy.where(parent_id: @distributer_proxy.policy.id)
-
if policies.empty?
-
notify_user(:alert, 'No children policies to distribute policy rules too.')
-
else
-
Delayed::Job.enqueue PolicyDistributerJob.new(@distributer_proxy, current_user), :priority => 0
-
-
notify_user(:notice, 'Parent policy distributed. Values updated to at least the minimum allowed by the parent policy.')
-
end
-
-
redirect_to policy_path(@policy)
-
end
-
-
private
-
-
# Never trust parameters from the scary internet, only allow the white list through.
-
def form_params
-
params.require(:policy).permit(policy_allowable_params)
-
end
-
-
def asset_subtype_rule_form_params
-
params.require(:policy_asset_subtype_rule).permit(policy_asset_subtype_rule_allowable_params)
-
end
-
-
def asset_type_rule_form_params
-
params.require(:policy_asset_type_rule).permit(policy_asset_type_rule_allowable_params)
-
end
-
-
def try_create_replace_with_rule rule
-
if (rule.try(:replace_asset_subtype_id) || rule.try(:replace_fuel_type_id))
-
if @policy.policy_asset_subtype_rules.find_by(asset_subtype_id: (rule.replace_asset_subtype_id || rule.asset_subtype_id), fuel_type_id: (rule.replace_fuel_type_id || rule.fuel_type_id)).nil?
-
policy_to_use = @policy.parent.present? ? @policy.parent : @policy
-
new_rule = policy_to_use.policy_asset_subtype_rules.find_by(asset_subtype_id: (rule.replace_asset_subtype_id || rule.asset_subtype_id)).dup
-
new_rule.policy = @policy # reset duplicated rule to agency's policy
-
new_rule.fuel_type_id = (rule.replace_fuel_type_id || rule.fuel_type_id)
-
if new_rule.save
-
if params[:edit_rule_after]
-
redirect_to show_edit_form_policy_path(@policy, :rule => new_rule.id, :type => 'asset_subtype', :notice => 'Please edit the policy rule corresponding to your Replace With assets.'), format: 'js'
-
return
-
end
-
end
-
-
end
-
end
-
-
render 'update_policy_rules'
-
end
-
-
def get_policy
-
@policy = Policy.find_by(:object_key => params[:id], :organization_id => (@organization_list + (defined?(Grantor).present? ? Grantor.ids : [])).uniq) unless params[:id].nil?
-
if @policy.nil?
-
if Policy.find_by(:object_key => params[:id], :organization_id => current_user.user_organization_filters.system_filters.first.get_organizations.map{|x| x.id}).nil?
-
redirect_to '/404'
-
else
-
notify_user(:warning, 'This record is outside your filter. Change your filter if you want to access it.')
-
redirect_to policies_path
-
end
-
return
-
end
-
-
end
-
-
def set_viewable_organizations
-
@viewable_organizations = current_user.viewable_organization_ids
-
-
get_organization_selections
-
end
-
-
end
-
class QueryFieldsController < TransamController
-
# TODO: Lock down the controller
-
# authorize_resource
-
-
respond_to :json
-
-
def index
-
-
@query_fields = QueryField.visible.order(:label)
-
unless params[:query_category_id].blank?
-
@query_fields = @query_fields.by_category_id(params[:query_category_id])
-
end
-
-
render json: @query_fields
-
end
-
end
-
class QueryFiltersController < OrganizationAwareController
-
# TODO: Lock down the controller
-
# authorize_resource
-
-
def render_new
-
@query_filter = QueryFilter.find_by_id(params[:query_filter_id])
-
if @query_filter
-
@query_field = @query_filter.query_field
-
# first check if this is a hidden filter
-
# if so, then need to get the main field
-
main_field = QueryField.where(pairs_with: @query_field.name).first
-
if main_field
-
@query_field = main_field
-
@pairs_with_filter = @query_filter
-
@query_filter = nil
-
else
-
if @query_field.pairs_with
-
# check if pairs_with field filter exist
-
@pairs_with_filter = @query_filter.saved_query.query_filters.joins(:query_field).where(query_fields: {name: @query_field.pairs_with}).first
-
end
-
end
-
else
-
@query_field = QueryField.find_by_id(params[:query_field_id])
-
end
-
end
-
-
# typeahead json response for manufacturer filter
-
def manufacturers
-
assets = TransamAsset.where(organization_id: @organization_list)
-
manufacturers = assets.joins(:manufacturer)
-
.where.not(manufacturer_id: nil)
-
.where.not(manufacturers: {name: 'Other'})
-
.pluck("manufacturers.id, manufacturers.name").uniq.map{|d| {id: d[0], name: d[1]}}
-
-
idx = 0 # indicate other_manufacturers
-
other_manufacturers = assets.where.not(other_manufacturer: [nil, ""]).pluck(:other_manufacturer).uniq.map{|name|
-
idx -= 1
-
{id: idx, name: name}
-
}
-
-
render json: (manufacturers + other_manufacturers).sort_by{|d| d[:name]}
-
end
-
-
# typeahead json response for manufacturer model filter
-
def manufacturer_models
-
assets = TransamAsset.where(organization_id: @organization_list)
-
manufacturer_models = assets.joins(:manufacturer_model)
-
.where.not(manufacturer_model_id: nil)
-
.where.not(manufacturer_models: {name: 'Other'})
-
.pluck("manufacturer_models.id, manufacturer_models.name").uniq.map{|d| {id: d[0], name: d[1]}}
-
-
idx = 0 # indicate other_manufacturers
-
other_manufacturer_models = assets.where.not(other_manufacturer_model: [nil, ""]).pluck(:other_manufacturer_model).uniq.map{|name|
-
idx -= 1
-
{id: idx, name: name}
-
}
-
-
render json: (manufacturer_models + other_manufacturer_models).sort_by{|d| d[:name]}
-
end
-
-
def vendors
-
assets = TransamAsset.where(organization_id: @organization_list)
-
-
vendors = Organization.pluck(:id, :name).uniq.map{|d| {id: d[0], name: d[1]}}
-
-
idx = 0 # indicate other
-
other_vendors = assets.where.not(other_vendor: nil).pluck(:other_vendor).uniq.map{|name|
-
idx -= 1
-
{id: idx, name: name}
-
}
-
-
render json: (vendors + other_vendors).sort_by{|d| d[:name]}
-
end
-
end
-
class ReportsController < OrganizationAwareController
-
-
before_action :get_report, :only => [:show, :load]
-
-
# Lock down the controller
-
#authorize_resource only: [:index, :show]
-
-
add_breadcrumb "Home", :root_path
-
add_breadcrumb "Reports", :reports_path
-
-
SESSION_VIEW_TYPE_VAR = 'reports_subnav_view_type'
-
-
def index
-
-
# remember the view type
-
@view_type = get_view_type(SESSION_VIEW_TYPE_VAR)
-
-
@reports = []
-
-
if params[:report_type]
-
active_reports = Report.active.where(report_type: params[:report_type])
-
else
-
active_reports = Report.active
-
end
-
-
active_reports.each do |rep|
-
if current_user.is_in_roles? rep.role_names
-
@reports << rep
-
end
-
end
-
-
end
-
-
def load
-
-
report_instance = @report.class_name.constantize.new
-
# inject the sql for the report into the params
-
params[:sql] = @report.custom_sql unless @report.custom_sql.blank?
-
# get the report data
-
@data = report_instance.get_data(@organization_list, params)
-
-
respond_to do |format|
-
format.js
-
format.json { render :json => @data.to_json }
-
end
-
-
end
-
-
def show
-
handle_show do
-
respond_to do |format|
-
format.html
-
format.xls do
-
sanitized_report_name = @report.name.gsub(" ", "_").underscore
-
response.headers['Content-Disposition'] = "attachment; filename=#{@organization.short_name}_#{sanitized_report_name}.xls"
-
end
-
end
-
end
-
end
-
-
#------------------------------------------------------------------------------
-
#
-
# Protected Methods
-
#
-
#------------------------------------------------------------------------------
-
protected
-
-
# Simply calling super in a subclassed show method doesn't work because of the respond_to
-
# This method does the work of setting up the report, allowing the subclass to pass
-
# a different respond_to block.
-
def handle_show &block
-
@report_filter_type = params[:report_filter_type]
-
@class_filter_type = params[:class_filter_type]
-
@asset_types = []
-
@class_types = []
-
AssetType.active.each do |at|
-
count = Rails.application.config.asset_base_class_name.constantize.where(organization_id: @organization_list, asset_subtype_id: at.id).count
-
if count > 0
-
@asset_types << [at.name, at.id]
-
end
-
end
-
-
if @report
-
@report_view = @report.view_name
-
add_breadcrumb @report.name
-
-
@report_instance = @report.class_name.constantize.new(params)
-
# inject the sql for the report into the params
-
params[:sql] = @report.custom_sql unless @report.custom_sql.blank?
-
# get the report data
-
@data = @report_instance.get_data(@organization_list, params)
-
@class_types = @report_instance.try(:get_classes)
-
# String return value indicates an error message.
-
if @data.is_a? String
-
notify_user(:alert, @data)
-
redirect_to report_path(@report)
-
else
-
yield
-
end
-
end
-
end
-
-
# Returns the selected report
-
def get_report
-
# load this report and create the report instance
-
report = Report.find(params[:id])
-
# check that the current use is authorized to view the report
-
unless report.nil?
-
if current_user.is_in_roles? report.role_names
-
@report = report
-
end
-
end
-
if @report.nil?
-
notify_user(:alert, "Can't find report.")
-
redirect_to '/404'
-
return
-
end
-
end
-
end
-
class RuleSetAwareController < OrganizationAwareController
-
-
add_breadcrumb "Home", :root_path
-
add_breadcrumb "Policies", :rule_sets_path
-
-
# set the @rule_set_class variable before any actions are invoked
-
before_action :get_rule_set_class
-
-
def copy
-
# use the name of the controller that inherits from the rule set aware controller to determine the variable name of the object we're duping
-
data_obj = eval("@#{params[:controller].singularize}")
-
-
new_data_obj = data_obj.dup
-
new_data_obj.object_key = nil
-
-
new_data_obj.save!
-
-
self.instance_variable_set('@new_'+new_data_obj.class.to_s.underscore, new_data_obj)
-
end
-
-
-
# this method is to take some rule set data model and distribute by dup of each org in that data model's HABTM org relationship
-
# override .dup at the model level if want to copy over any associations or other customizations
-
# -------------------------------------------------
-
# ASSUMPTIONS
-
# --------------------------------------------------
-
# make sure there is a variable set for the object being dupped - should be named similar to the controller name
-
# .organizations exists
-
# .organization exits and can be set
-
# has an object key
-
# assume that there's a distribute event in the state machine for the data object
-
# assume data obj has parent variable
-
-
# check if there might be a special distribute otherwise just dup
-
-
# if data model has recipients send notification/email (check model for .recipients .email_enabled? .notification.enabled?)
-
-
def distribute
-
# use the name of the controller that inherits from the rule set aware controller to determine the variable name of the object we're duping
-
data_obj = eval("@#{params[:controller].singularize}")
-
-
orgs = data_obj.try(:organizations) || []
-
orgs.each do |org|
-
new_data_obj = data_obj.try(:distribute) || data_obj.dup
-
new_data_obj.object_key = nil
-
new_data_obj.organization = org
-
new_data_obj.parent = data_obj
-
-
new_data_obj.save!
-
end
-
-
# fire workflow event if exists
-
if data_obj.class.event_names.include? 'distribute'
-
params[:event] = 'distribute'
-
fire_workflow_event
-
end
-
-
end
-
-
#-------------------------------------------------------------------------------
-
# Used by all form controllers to update the form status as it goes thorugh
-
# its workflows
-
#-------------------------------------------------------------------------------
-
def fire_workflow_event
-
-
# use name of controller to get class
-
klass = params[:controller].classify.constantize
-
-
# Check that this is a valid event name for the state machines
-
if klass.event_names.include? params[:event]
-
event_name = params[:event]
-
rule_set = klass.find_by(object_key: params[:id])
-
-
if rule_set.fire_state_event(event_name)
-
event = WorkflowEvent.new
-
event.creator = current_user
-
event.accountable = rule_set
-
event.event_type = event_name
-
event.save
-
-
recipients = (rule_set.recipients || [])
-
-
# send notifications/email
-
if rule_set.try(:email_enabled?)
-
recipients.each do |user|
-
msg = Message.new
-
msg.user = current_user
-
msg.organization = current_user.organization
-
msg.to_user = user
-
msg.subject = rule_set.try(:message_subject) || "Workflow Change for #{rule_set}"
-
msg.body = rule_set.try(:message_body) || "#{rule_set.to_s.titleize} has been #{rule_set.state.humanize}."
-
msg.priority_type = PriorityType.default
-
msg.message_template = rule_set.try(:message_template)
-
msg.active = rule_set.try(:message_template).try(:active)
-
msg.save
-
end
-
end
-
-
if rule_set.try(:notification_enabled?)
-
event_url = Rails.application.routes.url_helpers.rule_set_tam_policies_path(@rule_set_type)
-
notification = Notification.create(text: rule_set.try(:message_body) || "#{rule_set.to_s.titleize} has been #{rule_set.state.humanize}.", link: event_url, notifiable_type: 'Organization', notifiable_id: rule_set.organization_id )
-
-
recipients.each do |user|
-
UserNotification.create(notification: notification, user: user)
-
end
-
end
-
else
-
notify_user(:alert, "Could not #{event_name.humanize} form #{rule_set}")
-
end
-
else
-
notify_user(:alert, "#{params[:event_name]} is not a valid event for a #{klass.class.name}")
-
end
-
-
respond_to do |format|
-
format.html { redirect_back(fallback_location: root_path) }
-
format.js { render 'fire_workflow_event'}
-
end
-
-
end
-
-
-
#-------------------------------------------------------------------------------
-
protected
-
#-------------------------------------------------------------------------------
-
-
#-------------------------------------------------------------------------------
-
# Returns the selected form
-
#-------------------------------------------------------------------------------
-
def get_rule_set_class
-
# load this report and create the report instance
-
rule_set = RuleSet.find_by(:object_key => params[:rule_set_id]) unless params[:rule_set_id].blank?
-
# check that the current use is authorized to view the report
-
unless rule_set.nil?
-
@rule_set_type = rule_set
-
add_breadcrumb @rule_set_type, rule_set_path(@rule_set_type)
-
end
-
if @rule_set_type.nil?
-
notify_user(:alert, "Can't find rule set.")
-
redirect_to '/404'
-
return
-
end
-
end
-
-
private
-
-
def rule_set_params
-
params.require(@rule_set_type.class_name.underscore.to_sym).permit(@rule_set_type.class_name.constantize.allowable_params)
-
end
-
end
-
class RuleSetsController < OrganizationAwareController
-
add_breadcrumb "Home", :root_path
-
add_breadcrumb "Policies", :rule_sets_path
-
-
before_action :set_rule_set, only: [:show]
-
-
# GET /rule_sets
-
def index
-
@rule_sets = RuleSet.all
-
end
-
-
# GET /rule_sets/1
-
def show
-
-
if @rule_set.rule_set_aware
-
path = eval("rule_set_#{@rule_set.class_name.underscore.pluralize}_path('#{@rule_set.object_key}')")
-
else
-
path = eval("#{@rule_set.class_name.underscore.pluralize}_path")
-
end
-
-
redirect_to path
-
end
-
-
private
-
# Use callbacks to share common setup or constraints between actions.
-
def set_rule_set
-
@rule_set = RuleSet.find_by(object_key: params[:id])
-
end
-
-
end
-
class SavedQueriesController < OrganizationAwareController
-
-
before_action :set_saved_query, only: [:show, :update, :destroy, :export, :clone, :remove_from_orgs, :show_remove_form]
-
-
add_breadcrumb "Home", :root_path
-
-
# Lock down the controller
-
authorize_resource
-
-
# GET /saved_queries
-
# GET /saved_queries.json
-
def index
-
add_breadcrumb "Query", saved_queries_url
-
add_breadcrumb "Saved Queries"
-
-
# list own queries, plus shared from other orgs
-
own_querie_ids = current_user.saved_queries.pluck(:id)
-
shared_querie_ids = SavedQuery.joins(:organizations).where("organizations.id": current_user.organization_ids).pluck("saved_queries.id").uniq
-
-
@queries = SavedQuery.where(id: (own_querie_ids + shared_querie_ids).uniq).uniq
-
-
respond_to do |format|
-
format.html
-
format.json { render :json => @queries }
-
end
-
-
end
-
-
# GET /saved_queries/1
-
# GET /saved_queries/1.json
-
def show
-
add_breadcrumb "Query", saved_queries_url
-
add_breadcrumb @query.name
-
-
respond_to do |format|
-
format.html
-
format.json
-
end
-
-
end
-
-
# GET /saved_queries/new
-
def new
-
add_breadcrumb "Query", saved_queries_url
-
add_breadcrumb "New Query"
-
-
@query = SavedQuery.new
-
@query.created_by_user = current_user
-
@query.organization_list = @organization_list
-
end
-
-
def save_as
-
if params[:id].blank?
-
@query = SavedQuery.new
-
@query.created_by_user = current_user
-
else
-
@query = SavedQuery.find_by_object_key(params[:id])
-
end
-
end
-
-
# GET /saved_queries/1/clone
-
def clone
-
cloned_query = @query.clone!
-
cloned_query.organization_list = @organization_list
-
cloned_query.created_by_user = current_user
-
-
respond_to do |format|
-
if cloned_query.save
-
notify_user :notice, 'Query was successfully cloned.'
-
format.html { redirect_to saved_query_path(cloned_query) }
-
format.json { render :show, status: :created, location: cloned_query }
-
else
-
notify_user :alert, "Cannot clone this query because: " + cloned_query.errors.full_messages.join(';')
-
format.html { redirect_back(fallback_location: root_path) }
-
format.json { render json: cloned_query.errors, status: :unprocessable_entity }
-
end
-
end
-
end
-
-
# This is to remove a shared query from other org
-
def remove_from_orgs
-
if params[:saved_query] && !params[:saved_query][:organization_ids].blank?
-
# exclude query main org (where it's created)
-
to_remove_org_ids = params[:saved_query][:organization_ids].select{|r| !r.blank? && r != @query.created_by_user.try(:organization_id).to_s}
-
-
@query.organizations.delete(*to_remove_org_ids) if to_remove_org_ids.any?
-
end
-
-
redirect_to saved_queries_path
-
end
-
-
def show_remove_form
-
@to_remove_org_ids = (@query.organization_ids || []) & (current_user.organization_ids || []) - [@query.created_by_user.try(:organization_id)]
-
@to_remove_orgs = Organization.where(id: @to_remove_org_ids)
-
end
-
-
# POST /saved_queries
-
# POST /saved_queries.json
-
def create
-
@query = SavedQuery.new(saved_query_params.except(:query_field_ids, :query_filters))
-
@query.organization_list = @organization_list
-
@query.created_by_user = current_user
-
filter_data = saved_query_params[:query_filters] ? saved_query_params[:query_filters].to_unsafe_h.map{|r,v| v} : []
-
@query.parse_query_fields saved_query_params[:query_field_ids], filter_data
-
-
respond_to do |format|
-
if @query.save
-
notify_user :notice, 'Query was successfully saved.'
-
format.html { redirect_to saved_query_path(@query) }
-
format.json { render :show, status: :created, location: @query }
-
else
-
notify_user :alert, "Cannot save this query because: " + @query.errors.full_messages.join(';')
-
format.html { redirect_back(fallback_location: root_path) }
-
format.json { render json: @query.errors, status: :unprocessable_entity }
-
end
-
end
-
end
-
-
# PATCH/PUT /saved_queries/1
-
# PATCH/PUT /saved_queries/1.json
-
def update
-
@query.assign_attributes saved_query_params.except(:query_field_ids, :query_filters)
-
@query.updated_by_user = current_user
-
@query.organization_list = @organization_list
-
filter_data = saved_query_params[:query_filters] ? saved_query_params[:query_filters].to_unsafe_h.map{|r,v| v} : []
-
@query.parse_query_fields saved_query_params[:query_field_ids], filter_data
-
respond_to do |format|
-
if @query.save
-
-
notify_user :notice, 'Query was successfully updated.'
-
format.html { redirect_to saved_query_path(@query) }
-
format.json { render :show, status: :ok, location: @query }
-
else
-
notify_user :alert, "Cannot update this query because: " + @query.errors.full_messages.join(';')
-
format.html { redirect_back(fallback_location: root_path) }
-
format.json { render json: @query.errors, status: :unprocessable_entity }
-
end
-
end
-
end
-
-
# DELETE /saved_queries/1
-
# DELETE /saved_queries/1.json
-
def destroy
-
@query.destroy
-
respond_to do |format|
-
notify_user :notice, 'Query was successfully removed.'
-
format.html { redirect_to saved_queries_url }
-
format.json { head :no_content }
-
end
-
end
-
-
# POST /saved_queries/query
-
def query
-
@query = SavedQuery.new
-
@query.organization_list = @organization_list
-
@query.parse_query_fields saved_query_params[:query_field_ids], saved_query_params[:query_filters]
-
-
data_count = @query.data.size
-
render json: { count: data_count }
-
end
-
-
# GET /saved_queries/export_unsaved
-
# Use GET request in order to download file, POST won't work
-
# This also changes param structure, therefore, need to do some customized param parsing
-
def export_unsaved
-
@query = SavedQuery.new
-
@query.organization_list = @organization_list
-
-
# a bit dirty, but needed
-
filter_data = saved_query_params[:query_filters].to_h.map{|idx,filter_data| filter_data} unless saved_query_params[:query_filters].blank?
-
@query.parse_query_fields saved_query_params[:query_field_ids], filter_data
-
respond_to do |format|
-
format.html
-
format.csv do
-
render_csv("query_results_#{Time.current.strftime('%Y%m%d%H%M')}.csv")
-
end
-
end
-
end
-
-
def export
-
@query.organization_list = @organization_list
-
respond_to do |format|
-
format.html
-
format.csv do
-
render_csv("query_results_#{Time.current.strftime('%Y%m%d%H%M')}.csv")
-
end
-
end
-
end
-
-
-
private
-
# Use callbacks to share common setup or constraints between actions.
-
def set_saved_query
-
@query = SavedQuery.find_by(:object_key => params[:id])
-
@query.organization_list = @organization_list
-
if @query.nil?
-
redirect_to '/404'
-
return
-
end
-
end
-
-
# Never trust parameters from the scary internet, only allow the white list through.
-
def saved_query_params
-
params.require(:saved_query).permit(SavedQuery.allowable_params)
-
end
-
-
def render_csv(file_name)
-
set_file_headers file_name
-
set_streaming_headers
-
-
response.status = 200
-
-
#setting the body to an enumerator, rails will iterate this enumerator
-
self.response_body = csv_lines
-
end
-
-
-
def set_file_headers(file_name)
-
headers["Content-Type"] = "text/csv"
-
headers["Content-disposition"] = "attachment; filename=\"#{file_name}\""
-
end
-
-
-
def set_streaming_headers
-
#nginx doc: Setting this to "no" will allow unbuffered responses suitable for Comet and HTTP streaming applications
-
headers['X-Accel-Buffering'] = 'no'
-
-
headers["Cache-Control"] ||= "no-cache"
-
headers.delete("Content-Length")
-
end
-
-
def csv_lines
-
field_names = {}
-
field_types = {}
-
headers = []
-
@query.ordered_query_fields.each do |field|
-
headers << field.label
-
-
field_name = field.name
-
field_types[field_name] = field.filter_type
-
-
as_names = []
-
field.query_asset_classes.each do |qac|
-
as_names << "#{qac.table_name}_#{field_name}"
-
end
-
field_names[field_name] = as_names
-
end
-
-
# Excel is stupid if the first two characters of a csv file are "ID". Necessary to
-
# escape it. https://support.microsoft.com/kb/215591/EN-US
-
if headers.any? && headers[0].start_with?("ID")
-
headers = Array.new(headers)
-
headers[0] = "'" + headers[0]
-
end
-
-
Enumerator.new do |y|
-
CSV.generate do |csv|
-
unless field_names.blank?
-
y << headers.to_csv
-
-
# find_each would reduce memory usage, but it relies on valid primary_key
-
@query.data.find_each do |row|
-
row_data = field_names.map { |field_name, as_names|
-
val = nil
-
as_names.each do |as_name|
-
if row.send(as_name)
-
val = row.send(as_name)
-
break
-
end
-
end
-
-
if field_types[field_name] == 'boolean'
-
val = (val == 1 ? 'Yes' : 'No')
-
end
-
-
# special case
-
if field_name == 'replacement_status_type_id' && val.blank?
-
val = 'By Policy'
-
end
-
-
val
-
}
-
-
y << row_data.to_csv
-
end
-
-
end
-
end
-
end
-
-
end
-
end
-
class SavedSearchesController < OrganizationAwareController
-
-
before_action :set_search, only: [:show, :edit, :update, :destroy, :reorder]
-
-
add_breadcrumb "Home", :root_path
-
add_breadcrumb "Saved Searches", :saved_searches_path
-
-
# Lock down the controller
-
authorize_resource
-
-
# GET /saved_searches
-
# GET /saved_searches.json
-
def index
-
-
if params[:search_type_id]
-
@search_type = SearchType.find_by(id: params[:search_type_id].to_i)
-
@searches = current_user.all_searches(@search_type.try(:id))
-
else
-
@searches = current_user.all_searches
-
end
-
-
respond_to do |format|
-
format.html
-
format.json { render :json => @searches }
-
end
-
-
end
-
-
# GET /saved_searches/1
-
# GET /saved_searches/1.json
-
def show
-
-
# When loading a saved search we want to save the resulting search proxy
-
# to the session cache and re-direct to either the map search or work search
-
# page
-
-
respond_to do |format|
-
format.html {
-
search_proxy = @search.search_proxy
-
# cache the search proxy
-
Rails.logger.debug search_proxy.inspect
-
-
if search_proxy
-
search_type = @search.search_type
-
search_proxy[:organization_id] = @organization_list
-
searcher = search_type.class_name.constantize.new(search_proxy)
-
searcher.user = current_user
-
cache_objects(searcher.cache_params_variable_name, search_proxy)
-
cache_list(searcher.data, searcher.cache_variable_name)
-
-
redirect_to new_search_url(search_type: search_type.id), status: 303
-
end
-
}
-
format.js # load SQL in modal
-
end
-
-
end
-
-
# GET /saved_searches/new
-
def new
-
-
@search = SavedSearch.new(:search_type_id => params[:search_type_id])
-
end
-
-
# GET /saved_searches/1/edit
-
def edit
-
-
end
-
-
# POST /saved_searches
-
# POST /saved_searches.json
-
def create
-
-
@search = SavedSearch.new(saved_search_params)
-
@search.ordinal = SavedSearch.where(user: current_user).maximum(:ordinal).to_i + 1
-
@search.user = current_user
-
-
# Get the search proxy from the cache
-
search_proxy = get_cached_objects(@search.search_type.class_name.constantize.new.cache_params_variable_name)
-
-
# Save the name of the search
-
#search_proxy.name = @search.name
-
# serialize the search proxy to JSON
-
@search.json = search_proxy.to_json
-
searcher = @search.search_type.class_name.constantize.new(search_proxy)
-
searcher.user = current_user
-
@search.query_string = searcher.to_s #dont save org list. org filter is applied on top of queries
-
-
respond_to do |format|
-
if @search.save
-
notify_user :notice, 'Search was successfully saved.'
-
format.html { redirect_back(fallback_location: root_path) }
-
format.json { render :show, status: :created, location: @search }
-
else
-
notify_user :alert, "Cannot save this search because: " + @search.errors.full_messages.join(';')
-
format.html { redirect_back(fallback_location: root_path) }
-
format.json { render json: @search.errors, status: :unprocessable_entity }
-
end
-
end
-
end
-
-
# PATCH/PUT /saved_searches/1
-
# PATCH/PUT /saved_searches/1.json
-
def update
-
-
respond_to do |format|
-
if @search.update(saved_search_params)
-
notify_user :notice, 'Search was successfully updated.'
-
format.html { redirect_back(fallback_location: root_path) }
-
format.json { render :show, status: :ok, location: @search }
-
else
-
notify_user :alert, "Cannot update this search because: " + @search.errors.full_messages.join(';')
-
format.html { redirect_back(fallback_location: root_path) }
-
format.json { render json: @search.errors, status: :unprocessable_entity }
-
end
-
end
-
end
-
-
# DELETE /projects/1
-
# DELETE /projects/1.json
-
def destroy
-
@search.destroy
-
respond_to do |format|
-
notify_user :notice, 'Search was successfully removed.'
-
format.html { redirect_to saved_searches_url }
-
format.json { head :no_content }
-
end
-
end
-
-
#-----------------------------------------------------------------------------
-
# Reorders a search via AJAX. The saved search is identified by the object key.
-
#-----------------------------------------------------------------------------
-
def reorder
-
if params[:move]
-
if params[:move].to_i == -1
-
is_move_up = true
-
elsif params[:move].to_i == 1
-
is_move_down = true
-
end
-
end
-
-
if is_move_up || is_move_down
-
exchange_search = SavedSearch.find_by(:object_key => params[:target])
-
old_ordinal = @search.ordinal
-
if exchange_search
-
@search.ordinal = exchange_search.ordinal
-
@search.save(:validate => false)
-
-
exchange_search.ordinal = old_ordinal
-
exchange_search.save(:validate => false)
-
end
-
end
-
-
render :nothing => true, :status => 200, :content_type => 'text/html'
-
end
-
-
private
-
# Use callbacks to share common setup or constraints between actions.
-
def set_search
-
@search = SavedSearch.find_by(:object_key => params[:id])
-
if @search.nil?
-
redirect_to '/404'
-
return
-
end
-
end
-
-
# Never trust parameters from the scary internet, only allow the white list through.
-
def saved_search_params
-
params.require(:saved_search).permit(SavedSearch.allowable_params)
-
end
-
end
-
class SearchesController < OrganizationAwareController
-
-
add_breadcrumb "Home", :root_path
-
-
# Session Variables
-
INDEX_KEY_LIST_VAR = "search_key_list_cache_var"
-
-
MAX_ROWS_RETURNED = SystemConfig.instance.max_rows_returned
-
-
# Set the view variables form the params @search_type, @searcher_klass
-
before_action :set_view_vars, :only => [:create, :new, :reset]
-
-
def create
-
-
# use organization list if query is for any organization
-
params[:searcher][:organization_id] = @organization_list
-
-
@searcher = @searcher_klass.constantize.new(params[:searcher])
-
@searcher.user = current_user
-
@data = @searcher.data
-
-
# Cache the search params and result set so the use can page through them
-
unless @searcher.cache_params_variable_name.blank?
-
cache_objects(@searcher.cache_params_variable_name, params[:searcher])
-
end
-
unless @searcher.cache_variable_name.blank?
-
cache_list(@data, @searcher.cache_variable_name)
-
end
-
-
respond_to do |format|
-
format.html { render 'new' }
-
format.js { render 'new' }
-
format.json { render :json => @data }
-
end
-
-
end
-
-
# Render the inventory search form
-
def new
-
-
@searcher = @searcher_klass.constantize.new
-
unless @searcher.cache_params_variable_name.blank?
-
cached_search_params = get_cached_objects(@searcher.cache_params_variable_name)
-
@searcher = @searcher_klass.constantize.new(cached_search_params) unless cached_search_params.blank?
-
end
-
@searcher.user = current_user
-
-
cached_list = get_cached_objects(@searcher.cache_variable_name)
-
if cached_list.blank?
-
@data = []
-
else
-
@data = @searcher.cached_data(cached_list)
-
end
-
-
# check that an order param was provided otherwise use asset_tag as the default
-
params[:sort] ||= @searcher.default_sort
-
-
respond_to do |format|
-
format.html # new.html.haml this had been an erb and is now an haml the change should just be caught
-
format.json {
-
render :json => {
-
:total => @data.count,
-
:rows => @data.order("#{params[:sort]} #{params[:order]}").limit(params[:limit]).offset(params[:offset]).as_json(user: current_user, include_early_disposition: false)
-
}
-
}
-
end
-
end
-
-
def reset
-
@searcher = @searcher_klass.constantize.new
-
-
clear_cached_objects(@searcher.cache_params_variable_name)
-
cache_list([], @searcher.cache_variable_name)
-
-
redirect_to new_search_path(:search_type => @search_type.id)
-
end
-
-
# Action for performing full text search using the search text index
-
def keyword
-
-
@search_text = params["search_text"] ||= ""
-
-
add_breadcrumb "Keyword Search: '#{@search_text}'"
-
-
if @search_text.blank? or @search_text.length < 2
-
@keyword_search_results = KeywordSearchIndex.where("1 = 2")
-
else
-
-
# here we build the query one clause at a time based on the input params. The query
-
# is of the form:
-
#
-
# where organization_id IN (?) AND (search_text LIKE ? OR search_text_like ? OR ... )
-
-
where_clause = 'organization_id IN (?) AND ('
-
values = []
-
# The organization is scoped to search across all objects that are owned by
-
# the user's list of organizations
-
orgs = @organization_list.dup
-
# add org = 0 to get objects that are not indexed by org and are by
-
# contract available to users of all organizations
-
orgs << 0
-
values << orgs
-
-
search_params = []
-
@search_text.split(",").each_with_index do |search_string|
-
search_params << 'search_text LIKE ?'
-
values << "%#{search_string.strip}%"
-
end
-
-
where_clause << search_params.join(' OR ')
-
where_clause << ')'
-
-
@keyword_search_results = KeywordSearchIndex.where(where_clause, *values)
-
-
end
-
-
@num_rows = @keyword_search_results.count
-
cache_list(@keyword_search_results, INDEX_KEY_LIST_VAR)
-
-
respond_to do |format|
-
format.html
-
format.json {
-
render :json => {
-
:total => @num_rows,
-
:rows => data
-
}
-
}
-
end
-
-
end
-
-
protected
-
-
def data
-
res = []
-
@keyword_search_results.limit(params[:limit]).offset(params[:offset]).each do |row|
-
res << {
-
'content' => render_to_string(:partial => 'keyword_search_result_detail', :locals => {:search_result => row}).html_safe
-
}
-
end
-
res
-
end
-
-
def set_view_vars
-
-
@search_type = SearchType.find_by(id: params[:search_type])
-
-
if @search_type
-
-
add_breadcrumb "Query", new_search_path(:search_type => @search_type.id)
-
-
@searcher_klass = @search_type.class_name
-
add_breadcrumb @search_type.name
-
else
-
notify_user(:alert, "Something went wrong. Can't determine type of search to perform.")
-
return
-
end
-
-
end
-
-
end
-
1
class SessionsController < Devise::SessionsController
-
-
1
after_action :log_failed_login, :only => :new
-
1
after_action :clear_flash_messages, :only => [:create, :destroy]
-
1
before_action :log_logout, :only => :destroy
-
-
# determine which layout to use based on the current user state
-
1
layout :layout_by_resource
-
-
# Determine which layout to use based on the authorized state
-
1
def layout_by_resource
-
2
if user_signed_in?
-
1
"application"
-
else
-
1
"unauthorized"
-
end
-
end
-
-
# POST /resource/sign_in
-
1
def create
-
super
-
Rails.logger.info "Successful login with email : #{current_user.email} at #{Time.now}"
-
-
Rails.logger.debug "Configuring session for : #{current_user.name}"
-
-
# This must be configured in the Application Controller
-
create_user_session
-
-
Rails.logger.debug "Session configured"
-
-
end
-
-
1
protected
-
-
1
def clear_flash_messages
-
if flash.keys.include?(:notice)
-
flash.delete(:notice)
-
end
-
end
-
-
1
private
-
-
1
def log_failed_login
-
Rails.logger.info "Failed login with email: #{params['user']['email']} at #{Time.now}" if failed_login?
-
end
-
-
1
def failed_login?
-
(options = request.env["warden.options"]) && options[:action] == "unauthenticated"
-
end
-
-
1
def log_logout
-
Rails.logger.info "Logout for user with email: #{current_user.email} at #{Time.now}"
-
end
-
-
end
-
1
class SystemConfigsController < OrganizationAwareController
-
1
add_breadcrumb 'Home', :root_path
-
-
1
before_action :set_system_config
-
1
before_action :set_paper_trail_whodunnit
-
-
# GET /system_configs/1
-
1
def show
-
add_breadcrumb 'System Config', @system_config
-
end
-
-
1
def fiscal_year_rollover
-
add_breadcrumb 'Client Admin Interface', :client_admin_path
-
add_breadcrumb 'System Rollover', fiscal_year_rollover_system_config_path(@system_config)
-
end
-
-
# GET /system_configs/1/edit
-
1
def edit
-
add_breadcrumb 'System Config', @system_config
-
add_breadcrumb 'Edit', edit_system_config_path(@system_config)
-
end
-
-
# PATCH/PUT /system_configs/1
-
1
def update
-
if @system_config.update(system_config_params)
-
redirect_back(fallback_location: system_config_path(@system_config), notice: 'System config was successfully updated.')
-
else
-
render :edit
-
end
-
end
-
-
1
private
-
# Use callbacks to share common setup or constraints between actions.
-
1
def set_system_config
-
@system_config = SystemConfig.instance
-
end
-
-
# Only allow a trusted parameter "white list" through.
-
1
def system_config_params
-
params.require(:system_config).permit(SystemConfig.allowable_params)
-
end
-
-
end
-
1
class TasksController < NestedResourceController
-
-
1
add_breadcrumb "Home", :root_path
-
-
1
before_action :set_view_vars, :only => [:index, :filter]
-
1
before_action :set_task, :only => [:show, :edit, :update, :destroy, :fire_workflow_event, :change_owner]
-
1
before_action :reformat_date_field, :only => [:create, :update]
-
-
#-----------------------------------------------------------------------------
-
# Protect controller methods using the cancan ability
-
#-----------------------------------------------------------------------------
-
1
authorize_resource
-
-
# Ajax callback returning a list of tasks as JSON calendar events
-
1
def filter
-
filter_start_time = DateTime.parse(params[:start])
-
filter_end_time = DateTime.parse(params[:end])
-
-
# here we build the query one clause at a time based on the input params
-
clauses = []
-
values = []
-
if @select == "0"
-
clauses << ['assigned_to_user_id = ?']
-
values << current_user.id
-
else
-
clauses << ['organization_id = ?']
-
values << @organization.id
-
end
-
-
clauses << ['state IN (?)']
-
values << @states
-
-
clauses << ['complete_by BETWEEN ? AND ?']
-
values << filter_start_time
-
values << filter_end_time
-
-
tasks = Task.where(clauses.join(' AND '), *values).order("complete_by")
-
-
events = []
-
tasks.each do |t|
-
if t.assigned_to_user.nil?
-
task_title = t.subject
-
else
-
task_title = "(#{t.assigned_to_user.get_initials}) #{t.subject}"
-
end
-
events << {
-
:id => t.id,
-
:title => task_title,
-
:allDay => false,
-
:start => t.complete_by,
-
:url => user_task_path(current_user, t),
-
:className => get_state_css_class_name(t.state)
-
}
-
end
-
-
respond_to do |format|
-
format.json { render :json => events }
-
end
-
end
-
-
1
def fire_workflow_event
-
-
# Check that this is a valid event name for the state machines
-
1
if @task.class.event_names.include? params[:event]
-
1
event_name = params[:event]
-
1
if @task.fire_state_event(event_name)
-
1
event = WorkflowEvent.new
-
1
event.creator = current_user
-
1
event.accountable = @task
-
1
event.event_type = event_name
-
1
event.save
-
else
-
notify_user(:alert, "Could not #{event_name.humanize} task #{@task}")
-
end
-
else
-
notify_user(:alert, "#{params[:event_name]} is not a valid event for a #{@task.class.name}")
-
end
-
-
1
redirect_back(fallback_location: root_path)
-
-
end
-
-
1
def change_owner
-
-
1
user = User.find_by(:object_key => params[:user])
-
1
@task.assigned_to_user = user
-
1
@task.save
-
1
if user.nil?
-
notify_user(:notice, "Task is no longer assigned to anyone.")
-
else
-
1
notify_user(:notice, "Task is now assigned to #{user}")
-
end
-
-
1
redirect_back(fallback_location: root_path)
-
-
end
-
-
1
def index
-
-
1
add_breadcrumb "My Tasks", tasks_path
-
-
# Select tasks for this organization
-
1
@open_tasks = Task.where("organization_id = ? AND state IN (?)", @organization.id, Task.active_states).order("complete_by")
-
1
@closed_tasks = Task.where("organization_id = ? AND state IN (?)", @organization.id, Task.terminal_states).order("complete_by")
-
-
1
respond_to do |format|
-
1
format.html # index.html.erb
-
1
format.json { render :json => @tasks }
-
end
-
end
-
-
1
def show
-
-
# if not found or the object does not belong to the users
-
# send them back to index.html.erb
-
1
if @task.nil?
-
notify_user(:alert, "Record not found!")
-
redirect_to user_tasks_url
-
return
-
end
-
-
1
add_breadcrumb "My Tasks", tasks_path
-
1
add_breadcrumb @task.subject, task_path(@task)
-
-
1
respond_to do |format|
-
1
format.html # show.html.erb
-
1
format.json { render :json => @task }
-
end
-
end
-
-
1
def new
-
-
1
add_breadcrumb "My Tasks", tasks_path
-
1
add_breadcrumb 'New'
-
-
1
@taskable = find_resource
-
-
1
@task = Task.new
-
1
@task.user = current_user
-
1
@task.assigned_to_user = User.find_by_object_key(params[:assigned_to]) unless params[:assigned_to].nil?
-
1
@task.priority_type = PriorityType.default
-
-
1
respond_to do |format|
-
1
format.html # new.html.haml this had been an erb and is now an haml the change should just be caught
-
1
format.json { render :json => @task }
-
end
-
end
-
-
1
def edit
-
-
1
@taskable = find_resource
-
-
# if not found or the object does not belong to the users
-
# send them back to index.html.erb
-
-
1
add_breadcrumb "My Tasks", tasks_path
-
1
add_breadcrumb @task.subject, task_path(@task)
-
1
add_breadcrumb 'Update', edit_task_path(@task)
-
-
1
if @task.nil?
-
notify_user(:alert, "Record not found!")
-
redirect_to user_tasks_url
-
return
-
end
-
-
end
-
-
1
def create
-
-
1
@taskable = find_resource
-
1
if @taskable.nil?
-
@task = Task.new(form_params)
-
else
-
1
@task = @taskable.tasks.build(form_params)
-
end
-
-
-
-
# Simple form doesn't process mapped associations very well
-
#@task.assigned_to_user = User.find(params[:task][:assigned_to_user_id]) unless params[:task][:assigned_to_user_id].blank?
-
1
if @task.assigned_to_user.nil?
-
@task.organization = @taskable.organization
-
else
-
1
@task.organization = @task.assigned_to_user.organization
-
end
-
1
@task.user = current_user
-
-
1
add_breadcrumb "My Tasks", tasks_path
-
1
add_breadcrumb 'New'
-
-
1
respond_to do |format|
-
1
if @task.save
-
1
notify_user(:notice, "Task was successfully created.")
-
1
format.html {
-
# check where to redirect to
-
1
if URI(request.referer || '').path.include?('tasks')
-
1
redirect_to user_tasks_url(current_user)
-
else
-
redirect_back(fallback_location: root_path)
-
end
-
}
-
1
format.json { render :json => @task, :status => :created, :location => @task }
-
else
-
format.html { render :action => "new" }
-
format.json { render :json => @task.errors, :status => :unprocessable_entity }
-
end
-
end
-
-
end
-
-
1
def update
-
-
1
@taskable = @task.taskable
-
-
# if not found or the object does not belong to the users
-
# send them back to index.html.erb
-
1
if @task.nil?
-
notify_user(:alert, "Record not found!")
-
redirect_to user_tasks_url
-
return
-
end
-
-
1
add_breadcrumb "My Tasks", tasks_path
-
1
add_breadcrumb @task.subject, task_path(@task)
-
1
add_breadcrumb 'Update', edit_task_path(@task)
-
-
1
respond_to do |format|
-
1
if @task.update_attributes(form_params)
-
1
notify_user(:notice, "Task was successfully updated.")
-
2
format.html { redirect_to user_tasks_url(current_user) }
-
1
format.json { head :no_content }
-
else
-
format.html { render :action => "edit" }
-
format.json { render :json => @task.errors, :status => :unprocessable_entity }
-
end
-
end
-
end
-
-
1
def destroy
-
@task.destroy
-
notify_user(:notice, "Task was successfully removed.")
-
respond_to do |format|
-
format.html { redirect_back(fallback_location: root_path) }
-
format.json { head :no_content }
-
end
-
end
-
-
-
#------------------------------------------------------------------------------
-
#
-
# Protected Methods
-
#
-
#------------------------------------------------------------------------------
-
1
protected
-
-
# Sets the view variables
-
# @state - the state filter input in the request
-
# @states - an array of state names based on the @state
-
# @select - "0" = assigned to the user, "1" = assigned to the organization
-
1
def set_view_vars
-
1
if params[:state].blank? or params[:state] == "all"
-
1
@state = "all"
-
1
@states = Task.active_states
-
else
-
@state = params[:state]
-
@states = [@state]
-
end
-
-
1
if params[:select].blank?
-
1
@select = "0"
-
else
-
@select = params[:select]
-
end
-
end
-
-
1
def get_state_css_class_name(state)
-
if state == "new"
-
classname = 'alert alert-error'
-
elsif state == "started"
-
classname = 'alert alert-info'
-
elsif state == "complete"
-
classname = 'alert alert-success'
-
elsif state == "halted"
-
classname = 'alert'
-
else
-
classname = 'btn'
-
end
-
classname
-
end
-
-
#------------------------------------------------------------------------------
-
#
-
# Private Methods
-
#
-
#------------------------------------------------------------------------------
-
1
private
-
-
# Callbacks to share common setup or constraints between actions.
-
1
def set_task
-
5
@task = params[:id].nil? ? nil : Task.find_by(:object_key => params[:id])
-
5
if @task.nil?
-
redirect_to '/404'
-
return
-
end
-
end
-
-
# Never trust parameters from the scary internet, only allow the white list through.
-
1
def form_params
-
2
params.require(:task).permit(Task.allowable_params)
-
end
-
-
1
def reformat_date_field
-
2
unless params[:task][:complete_by].blank?
-
date_str = params[:task][:complete_by]
-
form_date = Date.strptime(date_str, '%m/%d/%Y')
-
params[:task][:complete_by] = form_date.strftime('%Y-%m-%d')
-
end
-
end
-
-
end
-
1
class TransamController < ApplicationController
-
-
1
before_action :authenticate_user!, :except => [:system_health]
-
1
before_action :set_timezone
-
1
before_action :log_session
-
-
# Include the rails4 style form parameters mixin
-
1
include TransamAttributes
-
-
# determine which layout to use based on the current user state
-
1
layout :layout_by_resource
-
-
1
OBJECT_CACHE_EXPIRE_SECONDS = Rails.application.config.object_cache_expire_seconds
-
-
# Enumerables for view types for index views
-
1
VIEW_TYPE_LIST = 1 # thumbnails
-
1
VIEW_TYPE_TABLE = 2 # table
-
1
VIEW_TYPE_MAP = 3 # map
-
-
1
ACTIVE_SESSION_LIST_CACHE_VAR = 'active_sessions_cache_key'
-
-
#-----------------------------------------------------------------------------
-
# A set of utilities to determine the database adapter type
-
#-----------------------------------------------------------------------------
-
1
def is_mysql
-
get_db_adapter == 'mysql2'
-
end
-
1
def is_postgresql
-
get_db_adapter == 'postgres'
-
end
-
# Returns the name of the database adapter
-
1
def get_db_adapter
-
ActiveRecord::Base.configurations[Rails.env]['adapter']
-
end
-
-
#-----------------------------------------------------------------------------
-
# Customize the 401 Access Denied error message
-
#-----------------------------------------------------------------------------
-
1
rescue_from CanCan::AccessDenied do |exception|
-
redirect_to '/401', :alert => exception.message
-
end
-
-
#-----------------------------------------------------------------------------
-
# Centralized message sender that can be overriden by an implementation
-
#-----------------------------------------------------------------------------
-
1
def notify_user(type, message, now = false)
-
# if there is a notify_user method in ApplicationController use it otherwise
-
# use this one
-
39
if defined?(super)
-
super
-
else
-
39
if now
-
flash.now[type] = message
-
else
-
39
flash[type] = message
-
end
-
end
-
end
-
-
#-----------------------------------------------------------------------------
-
# Determine which layout to use based on the authorized state
-
#-----------------------------------------------------------------------------
-
1
def layout_by_resource
-
71
if user_signed_in?
-
71
"application"
-
else
-
"unauthorized"
-
end
-
end
-
-
#-----------------------------------------------------------------------------
-
1
protected
-
#-----------------------------------------------------------------------------
-
-
#-----------------------------------------------------------------------------
-
# Stores the object keys of a list of objects in the session
-
#-----------------------------------------------------------------------------
-
1
def cache_list(objs, cache_key)
-
10
return if objs.nil?
-
10
begin
-
# attempt to just pluck the object_keys from the objects
-
10
list = objs.pluck(:object_key)
-
rescue
-
# but if the objects don't actually have an object_key field, call object_key on each
-
1
list = objs.map(&:object_key)
-
end
-
10
cache_objects(cache_key, list)
-
end
-
-
#-----------------------------------------------------------------------------
-
# Sets view vars @prev_record_key, @next_record_key, @total_rows and @row_number for
-
# a current object
-
#-----------------------------------------------------------------------------
-
1
def get_next_and_prev_object_keys(obj, cache_key)
-
7
@prev_record_key = nil
-
7
@next_record_key = nil
-
7
@total_rows = 0
-
7
@row_number = 0
-
7
id_list = get_cached_objects(cache_key)
-
# make sure we have a list and an object to find
-
7
if id_list && obj
-
7
@total_rows = id_list.size
-
# get the index of the current object in the array
-
7
current_index = id_list.index(obj.object_key)
-
7
if current_index
-
@row_number = current_index + 1
-
if current_index > 0
-
@prev_record_key = id_list[current_index - 1]
-
end
-
if current_index < id_list.size
-
@next_record_key = id_list[current_index + 1]
-
end
-
end
-
end
-
end
-
-
#-----------------------------------------------------------------------------
-
# Wrap the search text with db string search wildcards. This might need to be adjusted
-
# depending on the database being used.
-
#
-
# These work for MySQL
-
#
-
#-----------------------------------------------------------------------------
-
1
def get_search_value(search_text, search_type)
-
if search_type == "equals"
-
val = search_text
-
elsif search_type == "starts_with"
-
val = "#{search_text}%"
-
elsif search_type == "ends_with"
-
val = "%#{search_text}"
-
else # contains or any
-
val = "%#{search_text}%"
-
end
-
val
-
end
-
-
# Queues a job to be executed in the background
-
1
def fire_background_job(job, priority = 0)
-
Delayed::Job.enqueue job, :priority => priority
-
end
-
-
#-----------------------------------------------------------------------------
-
# Cache an object
-
#-----------------------------------------------------------------------------
-
1
def cache_objects(key, objects, expires_in = OBJECT_CACHE_EXPIRE_SECONDS)
-
10
Rails.logger.debug "ApplicationController CACHE put for key #{get_cache_key(current_user, key)}"
-
20
Rails.cache.fetch(get_cache_key(current_user, key), :force => true, :expires_in => expires_in) { objects }
-
end
-
-
#-----------------------------------------------------------------------------
-
# Return a cached object. If the object does not exist, an empty array is
-
# returned
-
#-----------------------------------------------------------------------------
-
1
def get_cached_objects(key)
-
7
Rails.logger.debug "ApplicationController CACHE get for key #{get_cache_key(current_user, key)}"
-
7
ret = Rails.cache.fetch(get_cache_key(current_user, key))
-
7
ret ||= []
-
end
-
-
#-----------------------------------------------------------------------------
-
# Clear an existing cache value
-
#-----------------------------------------------------------------------------
-
1
def clear_cached_objects(key)
-
Rails.logger.debug "ApplicationController CACHE clear for key #{get_cache_key(current_user, key)}"
-
Rails.cache.delete(get_cache_key(current_user, key))
-
end
-
-
#-----------------------------------------------------------------------------
-
# generates a cache key that is unique for a user and key name
-
#-----------------------------------------------------------------------------
-
1
def get_cache_key(user, key)
-
34
return "%06d:%s" % [user.id, key]
-
end
-
-
#-----------------------------------------------------------------------------
-
# returns the viewtype for the current controller and sets the session variable
-
# to store any change in view type for the controller
-
#-----------------------------------------------------------------------------
-
1
def get_view_type(session_var)
-
4
view_type = params[:view_type].nil? ? session[session_var].to_i : params[:view_type].to_i
-
4
if view_type.nil?
-
view_type = VIEW_TYPE_LIST
-
end
-
# remember the view type in the session
-
4
session[session_var] = view_type
-
4
return view_type
-
end
-
-
#
-
# Set the timezone for the session. If the user has one set in the database it is used
-
# otherwise one is defutled
-
1
def set_timezone
-
121
Time.zone = current_user.nil? ? 'Eastern Time (US & Canada)' : current_user.timezone
-
end
-
-
#-----------------------------------------------------------------------------
-
# Logs a session id in the cache with the time of access. If the session already
-
# exists the timestamp is updated with the time of this request otherwise
-
# the session is logged
-
#-----------------------------------------------------------------------------
-
1
def log_session
-
121
if user_signed_in?
-
121
key = "000000:#{ACTIVE_SESSION_LIST_CACHE_VAR}"
-
121
session_list = Rails.cache.fetch(key)
-
121
if session_list.blank?
-
h = {}
-
else
-
121
h = session_list
-
end
-
121
unless h.has_key? session.id
-
119
h[session.id] = {:start_time => Time.now, :views => 0, :user_id => current_user.id}
-
end
-
121
h[session.id][:last_view] = Time.now
-
121
h[session.id][:path] = request.env['ORIGINAL_FULLPATH']
-
121
h[session.id][:views] = h[session.id][:views].to_i + 1
-
121
h[session.id][:expire_time] = Time.now + current_user.timeout_in
-
121
h[session.id][:ip_addr] = request.remote_ip
-
242
Rails.cache.fetch(key, :force => true, :expires_in => 1.week) { h }
-
end
-
end
-
-
1
def user_for_paper_trail
-
current_user
-
end
-
-
-
end
-
class TransamWorkflowController < ApplicationController
-
-
-
def fire_workflow_event
-
if params[:transam_workflow_model_proxy]
-
event_proxy = TransamWorkflowModelProxy.new(workflow_params)
-
else
-
model_obj = params[:class_name].constantize.find_by(object_key: params[:object_key])
-
event_proxy = TransamWorkflowModelProxy.new(include_updates: 0, event_name: params[:event_name], global_ids: [model_obj.to_global_id])
-
end
-
-
-
process_workflow_event(event_proxy)
-
-
respond_to do |format|
-
format.html { redirect_back(fallback_location: root_path) }
-
format.js { render partial: "#{event_proxy.class_name.tabelize}/fire_workflow_events" }
-
end
-
end
-
-
# this method would change the state of a single or many model objects (starting an the same from_start)
-
# and if necessary, after doing some field/info updates to the model objects
-
def fire_workflow_events
-
params[:transam_workflow_model_proxy][:global_ids] = params[:transam_workflow_model_proxy][:global_ids].split(',')
-
event_proxy = TransamWorkflowModelProxy.new(workflow_params)
-
-
process_workflow_event(event_proxy)
-
-
respond_to do |format|
-
format.html { redirect_back(fallback_location: root_path) }
-
format.js { render partial: "#{event_proxy.class_name.tabelize}/fire_workflow_events" }
-
end
-
end
-
-
protected
-
-
def workflow_params
-
-
params.require(:transam_workflow_model_proxy).permit(TransamWorkflowModelProxy.allowable_params)
-
end
-
-
def workflow_model_params(class_name)
-
# if defined at the class or the instance level
-
params.require(:transam_workflow_model_proxy).permit(class_name.constantize.try(:allowable_params) || class_name.constantize.new.allowable_params)
-
end
-
-
def process_workflow_event(event_proxy)
-
if !event_proxy.model_objs.empty? && event_proxy.event_name.present?
-
Rails.logger.debug "fire_workflow_events event_name: #{event_proxy.event_name} for #{event_proxy.model_objs.count} instance(s)."
-
-
# Process each order sequentially
-
event_proxy.model_objs.each do |model_obj|
-
if (can? event_proxy.event_name.to_sym, model_obj) && (model_obj.class.event_names.include? event_proxy.event_name)
-
if event_proxy.include_updates.to_i > 0 && model_obj.class.event_transitions(event_proxy.event_name).map{|x| x.values}.flatten.include?(model_obj.state.to_sym)
-
model_obj.update!(workflow_model_params(event_proxy.class_name))
-
else
-
model_obj.transaction do
-
-
model_obj.update!(workflow_model_params(event_proxy.class_name)) if event_proxy.include_updates.to_i > 0
-
-
if model_obj.machine.fire_state_event(event_proxy.event_name)
-
WorkflowEvent.create(creator: current_user, accountable: model_obj, event_type: event_proxy.event_name)
-
else
-
raise ActiveRecord::Rollback
-
end
-
-
end
-
end
-
end
-
end
-
end
-
end
-
-
end
-
1
class UnlocksController < Devise::UnlocksController
-
-
# determine which layout to use based on the current user state
-
1
layout :layout_by_resource
-
-
# Determine which layout to use based on the authorized state
-
1
def layout_by_resource
-
2
if user_signed_in?
-
1
"application"
-
else
-
1
"unauthorized"
-
end
-
end
-
-
end
-
1
class UploadsController < OrganizationAwareController
-
-
1
add_breadcrumb "Home", :root_path
-
1
add_breadcrumb "Bulk Updates", :uploads_path
-
-
1
before_action :set_upload, :only => [:show, :destroy, :resubmit, :undo, :download]
-
-
# Lock down the controller
-
1
authorize_resource only: [:index, :show, :new, :create, :destroy]
-
-
# Session Variables
-
1
INDEX_KEY_LIST_VAR = "uploads_key_list_cache_var"
-
-
1
def index
-
-
-
# See if we got an file status type id
-
1
@file_status_type_id = params[:file_status_type_id]
-
1
if @file_status_type_id.blank?
-
1
@uploads = Upload.all
-
else
-
@file_status_type_id = @file_status_type_id.to_i
-
@uploads = Upload.where(file_status_type_id: @file_status_type_id)
-
-
type = FileStatusType.find(@file_status_type_id)
-
add_breadcrumb type.name unless type.nil?
-
end
-
-
# get assets from multi org or just uploaded by user (to get multi not yet processed)
-
1
asset_ids = Rails.application.config.asset_base_class_name.constantize.where.not(upload_id: nil).where(organization_id: @organization_list).pluck(:upload_id)
-
1
@uploads = @uploads.where('organization_id IN (?) OR id IN (?) OR user_id = ?', @organization_list, asset_ids, current_user.id).order(:created_at)
-
-
# cache the set of asset ids in case we need them later
-
1
cache_list(@uploads, INDEX_KEY_LIST_VAR)
-
-
1
respond_to do |format|
-
1
format.html # index.html.erb
-
1
format.json { render :json => @uploads }
-
end
-
-
end
-
-
1
def download
-
-
if @upload.nil?
-
notify_user(:alert, 'Record not found!')
-
redirect_to( root_path )
-
return
-
end
-
# read the attachment
-
content = open(@upload.file.url, "User-Agent" => "Ruby/#{RUBY_VERSION}") {|f| f.read}
-
# Send to the client
-
send_data content, :filename => @upload.original_filename
-
-
end
-
-
1
def show
-
-
1
if @upload.nil?
-
notify_user(:alert, "Record not found!")
-
redirect_to uploads_url
-
return
-
end
-
-
1
add_breadcrumb @upload.original_filename
-
-
# get the @prev_record_path and @next_record_path view vars
-
1
get_next_and_prev_object_keys(@upload, INDEX_KEY_LIST_VAR)
-
1
@prev_record_path = @prev_record_key.nil? ? "#" : upload_path(@prev_record_key)
-
1
@next_record_path = @next_record_key.nil? ? "#" : upload_path(@next_record_key)
-
-
1
respond_to do |format|
-
1
format.html # show.html.erb
-
1
format.json { render :json => @upload }
-
end
-
-
end
-
-
# Undo events created from the worksheet.
-
1
def undo
-
-
1
if @upload.nil?
-
notify_user(:alert, "Record not found!")
-
redirect_to uploads_url
-
return
-
end
-
-
1
@upload.reset
-
1
@upload.update(file_status_type: FileStatusType.find_by(name: "Reverted"))
-
-
1
notify_user(:notice, "Upload has been reverted.")
-
-
# show the original upload
-
1
redirect_to(upload_url(@upload))
-
-
end
-
-
# Reload the worksheet. This action pushes the spreadsheet back into the queue
-
# to be processed
-
1
def resubmit
-
-
if @upload.nil?
-
notify_user(:alert, "Record not found!")
-
redirect_to uploads_url
-
return
-
end
-
-
# Make sure the force flag is set and that the model is set back to
-
# unprocessed. reset destroys dependent asset_events
-
@upload.reset
-
@upload.save(:validate => false)
-
-
notify_user(:notice, "File was resubmitted for processing.")
-
-
# create a job to process this file in the background
-
create_upload_process_job(@upload)
-
-
# show the original upload
-
redirect_to(upload_url(@upload))
-
-
end
-
-
#-----------------------------------------------------------------------------
-
# Display the download template form. User can select the type of update they
-
# want and the list of asset types
-
#-----------------------------------------------------------------------------
-
1
def templates
-
-
1
add_breadcrumb "Download Template"
-
-
1
@message = "Creating inventory template. This process might take a while."
-
-
end
-
-
#-----------------------------------------------------------------------------
-
# Send a file to the user
-
#-----------------------------------------------------------------------------
-
1
def download_file
-
-
# Send it to the user
-
filename = params[:filename]
-
filepath = params[:filepath]
-
data = File.read(filepath)
-
send_data data, :filename => filename, :type => "application/vnd.ms-excel"
-
-
end
-
-
#-----------------------------------------------------------------------------
-
# Creates a spreadsheet template for bulk updating assets. There are two pathways
-
# to this method:
-
#
-
# template form -- sets the params[:template] param
-
# from an asset group -- sets the params[:file_content_type] param
-
#
-
#-----------------------------------------------------------------------------
-
1
def create_template
-
-
add_breadcrumb "Download Template"
-
-
# Figure out which approach was used to access this method
-
file_content_type = nil
-
# From the form. This is managed via a TemplateProxy class
-
if params[:template_proxy].present?
-
# Inflate the proxy
-
template_proxy = TemplateProxy.new(params[:template_proxy])
-
Rails.logger.debug template_proxy.inspect
-
-
# See if an org was set, use the default otherwise
-
if template_proxy.organization_id.blank?
-
org = nil
-
else
-
o = Organization.find(template_proxy.organization_id)
-
org = Organization.get_typed_organization(o)
-
end
-
-
# The form defines the FileContentType which identifies the builder to use
-
file_content_type = FileContentType.find(template_proxy.file_content_type_id)
-
# asset_types are an array of asset types
-
-
elsif params[:targets].present?
-
# The request came from the audit results page. We have a list of asset
-
# object keys
-
file_content_type = FileContentType.find(params[:file_content_type_id])
-
assets = Rails.application.config.asset_base_class_name.constantize.operational.where(:object_key => params[:targets].split(','))
-
org = nil
-
end
-
-
is_component = params[:is_component]
-
fta_asset_class_id = params[:fta_asset_class_id]
-
-
# Find out which builder is used to construct the template and create an instance
-
builder = file_content_type.builder_name.constantize.new(:organization => org, :asset_class_name => template_proxy.try(:asset_class_name), :asset_seed_class_id => template_proxy.try(:asset_seed_class_id), :organization_list => @organization_list, :is_component => is_component, :fta_asset_class_id => fta_asset_class_id)
-
-
# Generate the spreadsheet. This returns a StringIO that has been rewound
-
if params[:targets].present?
-
builder.assets = assets
-
end
-
stream = builder.build
-
-
# Save the template to a temporary file and render a success/download view
-
file = Tempfile.new ['template', '.tmp'], "#{Rails.root}/tmp"
-
ObjectSpace.undefine_finalizer(file)
-
#You can uncomment this line when debugging locally to prevent Tempfile from disappearing before download.
-
@filepath = file.path
-
@filename = "#{org.present? ? org.short_name.downcase : 'MultiOrg'}_#{file_content_type.class_name.underscore}_#{Date.today}.xlsx"
-
begin
-
file << stream.string
-
rescue => ex
-
Rails.logger.warn ex
-
ensure
-
file.close
-
end
-
# Ensure you're cleaning up appropriately...something wonky happened with
-
# Tempfiles not disappearing during testing
-
respond_to do |format|
-
format.js
-
format.html
-
end
-
-
end
-
-
1
def new
-
-
1
add_breadcrumb "New Template"
-
-
1
@upload = Upload.new
-
-
end
-
-
#-----------------------------------------------------------------------------
-
# Create a new upload. If the current user has a list of organizations, they can create
-
# an upload for any organization in their list
-
#-----------------------------------------------------------------------------
-
1
def create
-
-
1
@upload = Upload.new(form_params)
-
1
@upload.user = current_user
-
-
1
add_breadcrumb "New Template"
-
-
1
respond_to do |format|
-
1
if @upload.save
-
notify_user(:notice, "File was successfully uploaded.")
-
# create a job to process this file in the background
-
create_upload_process_job(@upload)
-
-
format.html { redirect_to uploads_url }
-
format.json { render :json => @upload, :status => :created, :location => @upload }
-
else
-
2
format.html { render :action => "new" }
-
1
format.json { render :json => @upload.errors, :status => :unprocessable_entity }
-
end
-
end
-
end
-
-
1
def destroy
-
-
1
if @upload.nil?
-
notify_user(:alert, "Record not found!")
-
redirect_to uploads_url
-
return
-
end
-
-
1
@upload.destroy
-
1
notify_user(:notice, "File was successfully removed.")
-
-
1
respond_to do |format|
-
2
format.html { redirect_to(uploads_url) }
-
1
format.json { head :no_content }
-
end
-
end
-
-
1
protected
-
-
# Generates a background job to propcess the file
-
1
def create_upload_process_job(upload, priority = 0)
-
if upload
-
job = UploadProcessorJob.new(upload.object_key)
-
fire_background_job(job, priority)
-
end
-
end
-
-
# Never trust parameters from the scary internet, only allow the white list through.
-
1
def form_params
-
1
params.require(:upload).permit(Upload.allowable_params)
-
end
-
-
# Callbacks to share common setup or constraints between actions.
-
1
def set_upload
-
3
@upload = Upload.find_by_object_key(params[:id]) unless params[:id].nil?
-
3
if @upload.nil?
-
redirect_to '/404'
-
return
-
end
-
end
-
-
1
private
-
-
end
-
1
class UserOrganizationFiltersController < OrganizationAwareController
-
-
1
add_breadcrumb "Home", :root_path
-
-
1
before_action :set_user_organization_filter, :only => [:show, :edit, :update, :destroy]
-
-
# GET /user_organization_filters
-
# GET /user_organization_filters.json
-
1
def index
-
-
1
add_breadcrumb "Organization Filters"
-
-
1
@user_organization_filters = current_user.user_organization_filters
-
-
end
-
-
# GET /user_organization_filters/1
-
# GET /user_organization_filters/1.json
-
1
def show
-
-
1
if @user_organization_filter.nil?
-
notify_user(:alert, 'Record not found!')
-
redirect_back(fallback_location: root_path)
-
return
-
end
-
-
1
add_breadcrumb "Organization Filters", user_user_organization_filters_path(current_user)
-
1
add_breadcrumb @user_organization_filter.name
-
-
end
-
-
#
-
1
def use
-
# Set the user's organization list for reporting and filtering to the list defined by
-
# the selected filter
-
-
1
@user_organization_filter = UserOrganizationFilter.find_by_object_key(params[:user_organization_filter_id])
-
-
1
if @user_organization_filter.nil?
-
notify_user(:alert, 'Record not found!')
-
redirect_back(fallback_location: root_path)
-
return
-
end
-
-
1
set_current_user_organization_filter_(current_user, @user_organization_filter)
-
-
# if currently on a filter detail page direct to detail page of filter just set
-
1
if URI(request.referer).path =~ /\/users\/[[:alnum:]]{12}\/user_organization_filters\/[[:alnum:]]{12}/
-
redirect_to user_user_organization_filter_path(current_user, @user_organization_filter)
-
else
-
1
redirect_back(fallback_location: root_path)
-
end
-
-
end
-
-
1
def set_org
-
@user_organization_filter = UserOrganizationFilter.find_by_object_key(params[:user_organization_filter_id])
-
-
if @user_organization_filter.nil?
-
notify_user(:alert, 'Record not found!')
-
redirect_back(fallback_location: root_path)
-
return
-
end
-
-
# Set the session variable to store the org selected
-
set_selected_organization_list(Organization.where(id: params[:org_user_organization_filter]))
-
-
redirect_back(fallback_location: root_path)
-
-
-
end
-
-
# GET /user_organization_filters/new
-
1
def new
-
-
1
add_breadcrumb "Organization Filters", user_user_organization_filters_path(current_user)
-
1
add_breadcrumb "New"
-
-
1
@user_organization_filter = UserOrganizationFilter.new
-
end
-
-
# GET /user_organization_filters/1/edit
-
1
def edit
-
-
1
if @user_organization_filter.nil?
-
notify_user(:alert, 'Record not found!')
-
redirect_back(fallback_location: root_path)
-
return
-
end
-
-
1
add_breadcrumb "Organization Filters", user_user_organization_filters_path(current_user)
-
1
add_breadcrumb @user_organization_filter.name, user_user_organization_filter_path(current_user, @user_organization_filter)
-
1
add_breadcrumb "Update"
-
-
-
end
-
-
# POST /user_organization_filters
-
# POST /user_organization_filters.json
-
1
def create
-
-
1
add_breadcrumb "Organization Filters", user_user_organization_filters_path(current_user)
-
1
add_breadcrumb "New"
-
-
1
@user_organization_filter = UserOrganizationFilter.new(form_params.except(:organization_ids))
-
1
@user_organization_filter.creator = current_user
-
1
if params[:share_filter]
-
@user_organization_filter.users = current_user.organization.users
-
@user_organization_filter.resource = current_user.organization
-
else
-
1
@user_organization_filter.users = [current_user]
-
end
-
-
# Add the organizations into the object. Make sure that the elements are unique so
-
# the same org is not added more than once.
-
1
if params[:query].to_i > 0
-
@user_organization_filter.query_string = QueryParam.find(params[:query].to_i).try(:query_string)
-
else
-
1
@user_organization_filter.query_string = nil
-
1
if form_params[:organization_ids].present?
-
1
org_list = form_params[:organization_ids].split(',').uniq
-
1
org_list.each do |id|
-
1
@user_organization_filter.organizations << Organization.find(id)
-
end
-
end
-
end
-
-
1
respond_to do |format|
-
1
if @user_organization_filter.save
-
-
1
if params[:commit] == "Update and Select This Filter"
-
set_current_user_organization_filter_(current_user, @user_organization_filter)
-
end
-
-
1
notify_user(:notice, 'Filter was successfully created.')
-
2
format.html { redirect_to [current_user, @user_organization_filter] }
-
1
format.json { render action: 'show', status: :created, location: @user_organization_filter }
-
else
-
format.html { render action: 'new' }
-
format.json { render json: @user_organization_filter.errors, status: :unprocessable_entity }
-
end
-
end
-
end
-
-
# PATCH/PUT /user_organization_filters/1
-
# PATCH/PUT /user_organization_filters/1.json
-
1
def update
-
-
1
if @user_organization_filter.nil?
-
notify_user(:alert, 'Record not found!')
-
redirect_back(fallback_location: root_path)
-
end
-
-
1
respond_to do |format|
-
1
if @user_organization_filter.update(form_params.except(:organization_ids))
-
-
# Add the (possibly) new organizations into the object
-
1
if params[:query].to_i > 0
-
# clear the existing list of organizations
-
@user_organization_filter.organizations.clear
-
-
@user_organization_filter.query_string = QueryParam.find(params[:query].to_i).try(:query_string)
-
else
-
1
@user_organization_filter.query_string = nil
-
1
if form_params[:organization_ids].present?
-
1
org_list = form_params[:organization_ids].split(',')
-
1
if org_list.count > 0
-
# clear the existing list of organizations
-
1
@user_organization_filter.organizations.clear
-
1
org_list.each do |id|
-
1
@user_organization_filter.organizations << Organization.find(id)
-
end
-
end
-
end
-
end
-
-
1
@user_organization_filter.save
-
-
1
if params[:share_filter]
-
@user_organization_filter.users = current_user.organization.users
-
@user_organization_filter.resource = current_user.organization
-
else
-
1
@user_organization_filter.users = [current_user]
-
1
@user_organization_filter.resource = nil
-
end
-
-
-
1
if params[:commit] == "Update and Select This Filter"
-
set_current_user_organization_filter_(current_user, @user_organization_filter)
-
end
-
-
-
1
notify_user(:notice, 'Filter was successfully updated.')
-
2
format.html { redirect_to [current_user, @user_organization_filter] }
-
1
format.json { head :no_content }
-
else
-
format.html { render action: 'edit' }
-
format.json { render json: @user_organization_filter.errors, status: :unprocessable_entity }
-
end
-
end
-
end
-
-
# DELETE /user_organization_filters/1
-
# DELETE /user_organization_filters/1.json
-
1
def destroy
-
1
@user_organization_filter.destroy
-
1
notify_user(:notice, 'Filter was successfully removed.')
-
1
respond_to do |format|
-
2
format.html { redirect_to user_user_organization_filters_path(current_user) }
-
1
format.json { head :no_content }
-
end
-
end
-
-
1
private
-
# Use callbacks to share common setup or constraints between actions.
-
1
def set_user_organization_filter
-
4
@user_organization_filter = UserOrganizationFilter.find_by_object_key(params[:id])
-
end
-
-
# Never trust parameters from the scary internet, only allow the white list through.
-
1
def form_params
-
6
params.require(:user_organization_filter).permit(UserOrganizationFilter.allowable_params)
-
end
-
end
-
1
class UsersController < OrganizationAwareController
-
-
#-----------------------------------------------------------------------------
-
# Protect controller methods using the cancan ability
-
#-----------------------------------------------------------------------------
-
1
authorize_resource :user, except: :popup
-
-
#-----------------------------------------------------------------------------
-
1
add_breadcrumb "Home", :root_path
-
1
add_breadcrumb "Users", :users_path
-
-
#-----------------------------------------------------------------------------
-
1
skip_before_action :get_organization_selections, :only => [:authorizations]
-
1
before_action :set_viewable_organizations, :only => [:authorizations]
-
-
-
1
before_action :set_user, :only => [:show, :edit, :settings, :update, :destroy, :change_password, :update_password, :profile_photo, :reset_password, :authorizations]
-
1
before_action :check_for_cancel, :only => [:create, :update, :update_password]
-
-
-
-
#-----------------------------------------------------------------------------
-
1
INDEX_KEY_LIST_VAR = "user_key_list_cache_var"
-
1
SESSION_VIEW_TYPE_VAR = 'users_subnav_view_type'
-
-
1
FILTERS_IGNORED = Rails.application.config.try(:user_organization_filters_ignored).present?
-
-
#-----------------------------------------------------------------------------
-
# GET /users
-
# GET /users.json
-
#-----------------------------------------------------------------------------
-
1
def index
-
-
3
@organization_id = params[:organization_id].to_i
-
3
@search_text = params[:search_text]
-
3
@role = params[:role]
-
3
@id_filter_list = params[:ids]
-
-
# Start to set up the query
-
3
conditions = []
-
3
values = []
-
-
3
if @organization_id.to_i > 0
-
conditions << 'users_organizations.organization_id = ?'
-
values << @organization_id
-
else
-
3
conditions << 'users_organizations.organization_id IN (?)'
-
3
values << @organization_list
-
end
-
-
-
3
unless @search_text.blank?
-
# get the list of searchable fields from the asset class
-
searchable_fields = User.new.searchable_fields
-
# create an OR query for each field
-
query_str = []
-
first = true
-
# parameterize the search based on the selected search parameter
-
search_value = get_search_value(@search_text, @search_param)
-
# Construct the query based on the searchable fields for the model
-
searchable_fields.each do |field|
-
if first
-
first = false
-
query_str << '('
-
else
-
query_str << ' OR '
-
end
-
-
query_str << "UPPER(users.#{field})"
-
query_str << ' LIKE ? '
-
# add the value in for this sub clause
-
values << search_value
-
end
-
query_str << ')' unless searchable_fields.empty?
-
-
conditions << [query_str.join]
-
end
-
-
3
unless @id_filter_list.blank?
-
conditions << 'object_key in (?)'
-
values << @id_filter_list
-
end
-
-
3
if params[:show_active_only].nil?
-
3
@show_active_only = 'active'
-
else
-
@show_active_only = params[:show_active_only]
-
end
-
-
3
if @show_active_only == 'active'
-
3
conditions << 'users.active = ?'
-
3
values << true
-
elsif @show_active_only == 'inactive'
-
conditions << 'users.active = ?'
-
values << false
-
end
-
-
# Get the Users but check to see if a role was selected
-
3
@users = User.unscoped.distinct.joins(:organizations).includes(:organization,:roles).where(conditions.join(' AND '), *values)
-
3
@users = @users.with_role(@role) unless @role.blank?
-
-
3
if params[:sort] && params[:order]
-
case params[:sort]
-
when 'organization_short_name'
-
@users = @users.joins(:organization).merge(Organization.order(short_name: params[:order]))
-
# figure out sorting by role + privilege some other way
-
# when 'role_name'
-
# @users = @users.joins(:roles).merge(Role.unscoped.order(name: params[:order]))
-
# when 'privilege_names'
-
# @users = @users.joins(:roles).merge(Role.order(privilege: params[:order]))
-
else
-
@users = @users.order(params[:sort] => params[:order])
-
end
-
else
-
3
@users = @users.order(:organization_id, :last_name)
-
end
-
-
# Set the breadcrumbs
-
3
if @organization_list.count == 1
-
org = Organization.find(@organization_list.first)
-
add_breadcrumb org.short_name, users_path(:organization_id => org.id)
-
end
-
3
if @role.present?
-
2
add_breadcrumb @role.titleize, users_path(:role => @role)
-
end
-
-
# remember the view type
-
3
@view_type = get_view_type(SESSION_VIEW_TYPE_VAR)
-
-
3
respond_to do |format|
-
3
format.html # index.html.erb
-
3
format.json {
-
render :json => {
-
:total => @users.count,
-
:rows => @users.limit(params[:limit]).offset(params[:offset]).collect{ |u|
-
u.as_json.merge!({
-
organization_short_name: u.organization.short_name,
-
organization_name: u.organization.name,
-
role_name: !@role.blank? && !Role.find_by(name: @role).privilege ? u.roles.roles.find_by(name: @role).label : u.roles.roles.last.label,
-
privilege_names: u.roles.privileges.collect{|x| x.label}.join(', '),
-
all_orgs: u.organizations.map{ |o| o.to_s }.join(', ')
-
})
-
}
-
}
-
}
-
-
end
-
end
-
-
#-----------------------------------------------------------------------------
-
# Show the list of current sessions. Only available for admin users
-
#-----------------------------------------------------------------------------
-
1
def sessions
-
-
key = "000000:#{TransamController::ACTIVE_SESSION_LIST_CACHE_VAR}"
-
@sessions = Rails.cache.fetch(key)
-
@sessions ||= {}
-
-
end
-
-
#-----------------------------------------------------------------------------
-
# GET /users/1
-
# GET /users/1.json
-
#-----------------------------------------------------------------------------
-
1
def show
-
-
1
if @user.id == current_user.id
-
1
add_breadcrumb "My Profile"
-
else
-
add_breadcrumb @user.name
-
end
-
-
# get the @prev_record_path and @next_record_path view vars
-
1
get_next_and_prev_object_keys(@user, INDEX_KEY_LIST_VAR)
-
1
@prev_record_path = @prev_record_key.nil? ? "#" : user_path(@prev_record_key)
-
1
@next_record_path = @next_record_key.nil? ? "#" : user_path(@next_record_key)
-
-
1
respond_to do |format|
-
1
format.html # show.html.erb
-
1
format.json { render :json => @user }
-
end
-
end
-
-
#-----------------------------------------------------------------------------
-
# GET /users/new
-
# GET /users/new.json
-
#-----------------------------------------------------------------------------
-
1
def new
-
-
1
add_breadcrumb 'New'
-
-
1
@user = User.new
-
-
1
respond_to do |format|
-
1
format.html # new.html.haml this had been an erb and is now an haml the change should just be caught
-
1
format.json { render :json => @user }
-
end
-
end
-
-
# GET /users/1/edit
-
1
def edit
-
-
2
add_user_breadcrumb('Profile')
-
2
add_breadcrumb 'Update'
-
-
end
-
-
1
def authorizations
-
add_breadcrumb @user, user_path(@user)
-
add_breadcrumb 'Update', authorizations_user_path(@user)
-
end
-
-
#-----------------------------------------------------------------------------
-
# Sends a reset password email to the user. This is an admin function
-
#-----------------------------------------------------------------------------
-
1
def reset_password
-
-
2
@user.send_reset_password_instructions
-
-
2
system_user = User.where(first_name: 'system', last_name: 'user').first
-
2
message_template = MessageTemplate.find_by(name: 'User2')
-
2
message_body = MessageTemplateMessageGenerator.new.generate(message_template,[@user.name, "<a href='#'>Change my password</a>"]).html_safe
-
-
2
msg = Message.new
-
2
msg.user = system_user
-
2
msg.organization = system_user.organization
-
2
msg.to_user = @user
-
2
msg.subject = message_template.subject
-
2
msg.body = message_body
-
2
msg.priority_type = message_template.priority_type
-
2
msg.message_template = message_template
-
2
msg.active = message_template.active
-
2
msg.save
-
-
2
notify_user(:notice, "Instructions for resetting their password was sent to #{@user} at #{@user.email}")
-
-
2
redirect_to user_path(@user)
-
-
end
-
-
#-----------------------------------------------------------------------------
-
# GET /users/1/edit
-
#-----------------------------------------------------------------------------
-
1
def change_password
-
-
2
add_user_breadcrumb('Profile')
-
2
add_breadcrumb 'Change Password'
-
-
end
-
-
#-----------------------------------------------------------------------------
-
#-----------------------------------------------------------------------------
-
1
def settings
-
-
2
add_user_breadcrumb('Profile')
-
2
add_breadcrumb 'Update'
-
end
-
-
#-----------------------------------------------------------------------------
-
# POST /users
-
# POST /users.json
-
#-----------------------------------------------------------------------------
-
1
def create
-
-
# Get the role_ids and privelege ids and remove them from the params hash
-
# as we dont want these managed by the rails associations
-
1
role_id = params[:user][:role_ids]
-
1
privilege_ids = params[:user][:privilege_ids]
-
-
# Get a new user service to invoke any business logic associated with creating
-
# new users
-
1
new_user_service = get_new_user_service
-
# Create the user
-
1
@user = new_user_service.build(form_params.except(:organization_ids))
-
-
1
if @user.organization.nil?
-
@user.organization_id = @organization_list.first
-
org_list = @organization_list
-
else
-
1
org_list = form_params[:organization_ids].split(',')
-
end
-
-
1
respond_to do |format|
-
1
if @user.save
-
-
# set organizations
-
1
@user.organizations = Organization.where(id: org_list)
-
-
# Perform an post-creation tasks such as sending emails, etc.
-
1
new_user_service.post_process @user
-
-
# Assign the role and privileges
-
1
role_service = get_user_role_service
-
1
role_service.set_roles_and_privileges @user, current_user, role_id, privilege_ids
-
1
role_service.post_process @user
-
-
1
notify_user(:notice, "User #{@user.name} was successfully created.")
-
2
format.html { redirect_to user_url(@user) }
-
1
format.json { render :json => @user, :status => :created, :location => @user }
-
else
-
format.html { render :action => "new" }
-
format.json { render :json => @user.errors, :status => :unprocessable_entity }
-
end
-
end
-
end
-
-
#-----------------------------------------------------------------------------
-
#-----------------------------------------------------------------------------
-
1
def update
-
-
2
add_user_breadcrumb('Profile')
-
2
add_breadcrumb 'Update'
-
-
# Get the role_ids and privelege ids and remove them from the params hash
-
# as we dont want these managed by the rails associations
-
2
role_id = params[:user][:role_ids]
-
2
privilege_ids = params[:user][:privilege_ids]
-
2
Rails.logger.debug "role_id = #{role_id}, privilege_ids = #{privilege_ids}"
-
-
2
respond_to do |format|
-
2
if @user.update_attributes(form_params.except(:organization_ids))
-
-
-
# Add the (possibly) new organizations into the object
-
2
if form_params[:organization_ids].present?
-
org_list = form_params[:organization_ids].split(',')
-
@user.organizations = Organization.where(id: org_list)
-
end
-
-
2
new_user_service = get_new_user_service
-
# Perform an post-creation tasks such as sending emails, etc.
-
2
new_user_service.post_process @user, true
-
-
#-----------------------------------------------------------------------
-
# Assign the role and privileges but only on a profile form, not a
-
# settings form
-
#-----------------------------------------------------------------------
-
2
unless role_id.blank?
-
2
role_service = get_user_role_service
-
2
role_service.set_roles_and_privileges @user, current_user, role_id, privilege_ids
-
2
role_service.post_process @user
-
end
-
-
2
if @user.id == current_user.id
-
1
notify_user(:notice, "Your profile was successfully updated.")
-
else
-
1
notify_user(:notice, "#{@user.name}'s profile was successfully updated.")
-
end
-
4
format.html { redirect_to user_url(@user) }
-
2
format.json { head :no_content }
-
else
-
format.html { render :action => "edit" }
-
format.json { render :json => @user.errors, :status => :unprocessable_entity }
-
end
-
-
end
-
end
-
-
#-----------------------------------------------------------------------------
-
#-----------------------------------------------------------------------------
-
1
def update_password
-
-
1
add_user_breadcrumb('Profile')
-
1
add_breadcrumb 'Change Password'
-
-
1
respond_to do |format|
-
1
if @user.update_with_password(form_params)
-
# automatically sign in the user bypassing validation
-
if @user.id == current_user.id
-
notify_user(:notice, "Your password was successfully updated.")
-
else
-
notify_user(:notice, "#{@user.name}'s password was successfully updated.")
-
end
-
sign_in @user, :bypass => true
-
format.html { redirect_to user_url(@user) }
-
format.json { head :no_content }
-
else
-
2
format.html { render :action => "change_password" }
-
1
format.json { render :json => @user.errors, :status => :unprocessable_entity }
-
end
-
end
-
end
-
-
-
#-----------------------------------------------------------------------------
-
#-----------------------------------------------------------------------------
-
1
def destroy
-
1
if @user.active
-
1
@user.active = false
-
1
@user.notify_via_email = false
-
1
activation = "deactivated"
-
else
-
@user.active = true
-
@user.notify_via_email = true
-
activation = "reactivated"
-
end
-
1
@user.save(:validate => false)
-
1
respond_to do |format|
-
1
notify_user(:notice, "User #{@user} has been #{activation}.")
-
2
format.html { redirect_to user_url(@user) }
-
1
format.json { head :no_content }
-
end
-
end
-
-
#-----------------------------------------------------------------------------
-
#-----------------------------------------------------------------------------
-
1
def profile_photo
-
2
add_user_breadcrumb('Profile')
-
2
add_breadcrumb 'Profile Photo'
-
end
-
-
1
def popup
-
@user = User.find_by_object_key(params[:id])
-
end
-
-
#------------------------------------------------------------------------------
-
# Protected Methods
-
#------------------------------------------------------------------------------
-
1
protected
-
-
1
def set_viewable_organizations
-
if current_user.has_role? :admin
-
@viewable_organizations = Organization.ids
-
else
-
@viewable_organizations = current_user.viewable_organization_ids
-
end
-
-
get_organization_selections
-
end
-
-
-
#------------------------------------------------------------------------------
-
# Private Methods
-
#------------------------------------------------------------------------------
-
1
private
-
-
#-----------------------------------------------------------------------------
-
# Never trust parameters from the scary internet, only allow the white list through.
-
#-----------------------------------------------------------------------------
-
1
def form_params
-
# Remove role and privilege ids as these are managed by the app not by
-
# the active record associations
-
7
params[:user].delete :role_ids
-
7
params[:user].delete :privilege_ids
-
7
params.require(:user).permit(user_allowable_params)
-
end
-
-
#-----------------------------------------------------------------------------
-
# Callbacks to share common setup or constraints between actions.
-
#-----------------------------------------------------------------------------
-
1
def set_user
-
-
15
if params[:id] == current_user.object_key
-
8
@user = User.find_by(:object_key => params[:id])
-
7
elsif FILTERS_IGNORED
-
@user = User.unscoped.find_by(:object_key => params[:id])
-
-
if @user.nil?
-
redirect_to '/404'
-
end
-
else
-
7
@user = User.unscoped.find_by(:object_key => params[:id], :organization_id => @organization_list)
-
-
7
if @user.nil?
-
if User.find_by(:object_key => params[:id], :organization_id => current_user.user_organization_filters.system_filters.first.get_organizations.map{|x| x.id}).nil?
-
redirect_to '/404'
-
else
-
notify_user(:warning, 'This record is outside your filter. Change your filter if you want to access it.')
-
redirect_to users_path
-
end
-
end
-
-
end
-
-
return
-
end
-
-
#-----------------------------------------------------------------------------
-
1
def add_user_breadcrumb(page)
-
11
if @user.id == current_user.id
-
6
add_breadcrumb "My #{page}", user_path(@user)
-
else
-
5
add_breadcrumb @user.name, user_path(@user)
-
end
-
end
-
-
#-----------------------------------------------------------------------------
-
1
def check_for_cancel
-
4
unless params[:cancel].blank?
-
redirect_to user_url(current_user)
-
end
-
end
-
-
#-----------------------------------------------------------------------------
-
# Get the configured service to handle user creation, defaulting
-
#-----------------------------------------------------------------------------
-
1
def get_new_user_service
-
3
Rails.application.config.new_user_service.constantize.new
-
end
-
-
#-----------------------------------------------------------------------------
-
# Get the configured service to handle user role management, defaulting
-
#-----------------------------------------------------------------------------
-
1
def get_user_role_service
-
3
Rails.application.config.user_role_service.constantize.new
-
end
-
end
-
1
class VendorsController < OrganizationAwareController
-
-
1
add_breadcrumb "Home", :root_path
-
1
add_breadcrumb "Vendors", :vendors_path
-
-
1
before_action :set_vendor, only: [:show, :edit, :update, :destroy]
-
-
# Lock down the controller
-
1
authorize_resource only: [:index, :show, :new, :create, :edit, :update, :destroy]
-
-
# include the transam markers mixin
-
1
include TransamMapMarkers
-
-
# Session Variables
-
1
INDEX_KEY_LIST_VAR = "vendors_list_cache_var"
-
-
# GET /vendors
-
1
def index
-
-
# Start to set up the query
-
1
conditions = []
-
1
values = []
-
-
# Limit to the organization list
-
1
conditions << 'organization_id IN (?)'
-
1
values << @organization_list
-
-
1
@vendors = Vendor.where(conditions.join(' AND '), *values).order(:name)
-
-
# cache the vendor ids in case we need them later
-
1
cache_list(@vendors, INDEX_KEY_LIST_VAR)
-
-
end
-
-
1
def filter
-
1
query = params[:query]
-
1
query_str = "%" + query + "%"
-
1
Rails.logger.debug query_str
-
-
1
matches = []
-
1
vendors = Vendor.active.where("organization_id in (?) AND (name LIKE ?)", @organization_list, query_str)
-
1
vendors.each do |vendor|
-
1
matches << {
-
"id" => vendor.id,
-
"name" => vendor.name
-
}
-
end
-
-
1
respond_to do |format|
-
1
format.js { render :json => matches.to_json }
-
2
format.json { render :json => matches.to_json }
-
end
-
-
end
-
-
# GET /vendors/1
-
1
def show
-
-
1
add_breadcrumb @vendor
-
-
1
@vendors = []
-
1
@vendors << @vendor
-
1
@markers = generate_map_markers(@vendors, true)
-
-
# get the @prev_record_path and @next_record_path view vars
-
1
get_next_and_prev_object_keys(@vendor, INDEX_KEY_LIST_VAR)
-
1
@prev_record_path = @prev_record_key.nil? ? "#" : vendor_path(@prev_record_key)
-
1
@next_record_path = @next_record_key.nil? ? "#" : vendor_path(@next_record_key)
-
-
end
-
-
# GET /vendors/new
-
1
def new
-
-
1
add_breadcrumb "New", new_vendor_path
-
-
1
@vendor = Vendor.new
-
-
end
-
-
# GET /vendors/1/edit
-
1
def edit
-
-
1
add_breadcrumb @vendor.name, vendor_path(@vendor)
-
1
add_breadcrumb "Update"
-
-
end
-
-
# POST /vendors
-
# POST /vendors.json
-
1
def create
-
-
1
add_breadcrumb "New", new_vendor_path
-
-
1
@vendor = Vendor.new(vendor_params)
-
1
@vendor.organization = @organization
-
-
1
respond_to do |format|
-
1
if @vendor.save
-
1
notify_user(:notice, "The vendor was successfully saved.")
-
2
format.html { redirect_to vendor_url(@vendor) }
-
1
format.json { render action: 'show', status: :created, location: @vendor }
-
else
-
format.html { render action: 'new' }
-
format.json { render json: @vendor.errors, status: :unprocessable_entity }
-
end
-
end
-
end
-
-
# PATCH/PUT /vendors/1
-
# PATCH/PUT /vendors/1.json
-
1
def update
-
-
1
add_breadcrumb @vendor.name, vendor_path(@vendor)
-
1
add_breadcrumb "Update"
-
-
1
respond_to do |format|
-
1
if @vendor.update(vendor_params)
-
1
notify_user(:notice, "The vendor was successfully updated.")
-
2
format.html { redirect_to vendor_url(@vendor) }
-
1
format.json { head :no_content }
-
else
-
format.html { render action: 'edit' }
-
format.json { render json: @vendor.errors, status: :unprocessable_entity }
-
end
-
end
-
end
-
-
1
def destroy
-
1
if @vendor.destroy
-
1
redirect_to vendors_url, notice: 'Vendor was successfully destroyed.'
-
else
-
redirect_to @vendor, notice: 'Vendor cannot be destroyed as there are assets and expenditures associated with it.'
-
end
-
end
-
-
1
private
-
#
-
# generate an array of map markers for use with the leaflet plugin
-
#
-
1
def generate_map_markers(vendors_array, render_open = false)
-
1
objs = []
-
1
vendors_array.each do |org|
-
1
objs << get_geocoded_marker(org, render_open) unless org.latitude.nil?
-
end
-
1
return objs.to_json
-
end
-
-
# Use callbacks to share common setup or constraints between actions.
-
1
def set_vendor
-
4
@vendor = Vendor.find_by(:object_key => params[:id])
-
4
if @vendor.nil?
-
redirect_to '/404'
-
return
-
end
-
-
end
-
-
1
def vendor_params
-
2
params.require(:vendor).permit(Vendor.allowable_params)
-
end
-
-
end
-
#------------------------------------------------------------------------------
-
#
-
# AbstractFileHandler
-
#
-
# Base class for file handlers
-
#
-
#------------------------------------------------------------------------------
-
class AbstractFileHandler
-
-
# Errors are trapped and handled by the Job that executes this handler
-
def execute
-
-
# Indicate that processing is starting
-
@upload.processing_started_at = Time.current
-
@upload.file_status_type = FileStatusType.find_by_name("In Progress")
-
@upload.save
-
-
begin
-
# process the file. This method is responsible for setting the results
-
process(@upload)
-
rescue => e
-
# Record the error message in the processing log
-
add_processing_message(1, 'error', "#{e}")
-
@new_status = FileStatusType.find_by_name("Errored")
-
ensure
-
# update the model with the results of the processing
-
@upload.file_status_type = @new_status
-
@upload.num_rows_processed = @num_rows_processed
-
@upload.num_rows_added = @num_rows_added
-
@upload.num_rows_replaced = @num_rows_replaced
-
@upload.num_rows_failed = @num_rows_failed
-
@upload.num_rows_skipped = @num_rows_skipped
-
@upload.processing_log = @process_log.to_s
-
-
@upload.processing_completed_at = Time.current
-
@upload.save
-
end
-
-
end
-
-
def can_process?
-
if @upload.nil?
-
add_processing_message(1, 'error', "Upload is missing or invalid.")
-
end
-
if @upload.file.url.blank?
-
add_processing_message(1, 'error', "File URL can't be blank.")
-
end
-
# return true or false depending on if errors were generated
-
@process_log.empty?
-
end
-
-
# Passthru for backwards compatibility with existing processing
-
def add_processing_message(level, severity, text)
-
@process_log.add_processing_message(level, severity, text)
-
end
-
-
-
protected
-
-
# Returns true if a block of cells are blank, false otherwise
-
def empty_block? cells, start_range, stop_range
-
(start_range..stop_range).each do |col|
-
if cells[col].present?
-
return false
-
end
-
end
-
true
-
end
-
-
# Runs a block-specific loader
-
def process_loader asset, klass, cells
-
loader = klass.new
-
-
# Populate the characteristics from the row
-
loader.process(asset, cells)
-
if loader.errors?
-
row_errored = true
-
loader.errors.each { |e| add_processing_message(2, 'warning', e)}
-
end
-
if loader.warnings?
-
loader.warnings.each { |e| add_processing_message(2, 'info', e)}
-
end
-
end
-
-
# Records an asset event against an asset
-
def record_event asset, event_message, klass, cells
-
-
loader = klass.new
-
loader.process(asset, cells)
-
-
# Only proceed if an event was generated
-
if loader.event.nil?
-
add_processing_message(3, 'success', "Nothing to do.")
-
return
-
end
-
-
if loader.errors?
-
row_errored = true
-
loader.errors.each { |e| add_processing_message(3, 'warning', e)}
-
end
-
if loader.warnings?
-
loader.warnings.each { |e| add_processing_message(3, 'info', e)}
-
end
-
# Check for any validation errors
-
event = loader.event
-
if event.valid?
-
event.save
-
add_processing_message(3, 'success', "#{event_message} added.")
-
else
-
Rails.logger.info "#{event_message} did not pass validation."
-
event.errors.full_messages.each { |e| add_processing_message(3, 'warning', e)}
-
end
-
-
end
-
-
-
def is_number?(val)
-
Float(val) != nil rescue false
-
end
-
-
private
-
def initialize(*args)
-
@process_log = ProcessLog.new
-
end
-
-
end
-
#------------------------------------------------------------------------------
-
#
-
# Condition Udpate Event Laoder
-
#
-
# Used to process both SOGR updates and new inventory updates
-
#
-
#------------------------------------------------------------------------------
-
class ConditionUpdateEventLoader < EventLoader
-
-
CONDITION_RATING_COL = 0
-
EVENT_DATE_COL = 1
-
-
def process(asset, cells)
-
-
# Create a new ConditionUpdateEvent
-
@event = asset.build_typed_event(ConditionUpdateEvent)
-
-
# Condition Rating
-
@event.assessed_rating = as_float(cells[CONDITION_RATING_COL]) if cells[CONDITION_RATING_COL]
-
-
# Event Date
-
@event.event_date = as_date(cells[EVENT_DATE_COL])
-
-
-
end
-
-
private
-
def initialize
-
super
-
end
-
-
end
-
#------------------------------------------------------------------------------
-
#
-
# DispositionUpdateEventLoader
-
#
-
# Generic class for processing usage update events
-
#
-
#------------------------------------------------------------------------------
-
class DispositionUpdateEventLoader < EventLoader
-
-
EVENT_DATE_COL = 1
-
DISPOSITION_TYPE_COL = 0
-
SALES_PROCEEDS_COL = 2
-
MILEAGE_COL = 3
-
-
def process(asset, cells)
-
-
# Create a new DispositionUpdateEvent
-
@event = asset.build_typed_event(DispositionUpdateEvent)
-
-
# Event Date
-
@event.event_date = as_date(cells[EVENT_DATE_COL])
-
-
# Disposition Type
-
val = as_string(cells[DISPOSITION_TYPE_COL])
-
@event.disposition_type = DispositionType.search(val)
-
if @event.disposition_type.nil?
-
@errors << "Dispositon Type not found or missing. Type = #{val}."
-
end
-
-
# Sales Proceeds
-
@event.sales_proceeds = as_integer(cells[SALES_PROCEEDS_COL])
-
-
end
-
-
private
-
def initialize
-
super
-
end
-
-
end
-
#------------------------------------------------------------------------------
-
#
-
# DispositionUpdatesFileHandler
-
#
-
# Generic class for processing disposition updates for assets from a spreadsheet.
-
#
-
# This processes a file that has been exported as a template from the TransAM
-
# application
-
#
-
#------------------------------------------------------------------------------
-
class DispositionUpdatesFileHandler < AbstractFileHandler
-
-
OBJECT_KEY_COL = 0
-
ASSET_TAG_COL = 2
-
-
NUM_HEADER_ROWS = 2
-
SHEET_NAME = "Updates"
-
-
# Perform the processing
-
def process(upload)
-
-
@num_rows_processed = 0
-
@num_rows_added = 0
-
@num_rows_skipped = 0
-
@num_rows_replaced = 0
-
@num_rows_failed = 0
-
-
# Get the pertinent info from the upload record
-
file_url = upload.file.url # Usually stored on S3
-
organization = upload.organization # Organization who owns the assets
-
system_user = User.find(1) # System user is always the first user
-
-
add_processing_message(1, 'success', "Updating asset dispositions from '#{upload.original_filename}'")
-
add_processing_message(1, 'success', "Start time = '#{Time.now}'")
-
-
# Open the spreadsheet and start to process the asset events
-
begin
-
-
reader = SpreadsheetReader.new(file_url)
-
reader.open(SHEET_NAME)
-
-
Rails.logger.info " File Opened."
-
Rails.logger.info " Num Rows = #{reader.num_rows}, Num Cols = #{reader.num_cols}, Num Header Rows = #{NUM_HEADER_ROWS}"
-
-
# Process each row
-
count_blank_rows = 0
-
first_row = NUM_HEADER_ROWS + 1
-
first_row.upto(reader.last_row) do |row|
-
# Read the next row from the spreadsheet
-
cells = reader.read(row)
-
if reader.empty_row?
-
count_blank_rows += 1
-
if count_blank_rows > 10
-
break
-
end
-
else
-
notes = []
-
count_blank_rows = 0
-
@num_rows_processed += 1
-
-
# Get the asset by the object key
-
object_key = cells[OBJECT_KEY_COL].to_s
-
# asset tags are sometimes stored as numbers
-
asset_tag = cells[ASSET_TAG_COL].to_s
-
-
Rails.logger.debug " Processing row #{row}. Asset ID = '#{object_key}', Asset Tag = '#{asset_tag}'"
-
-
if object_key.present?
-
asset = Rails.application.config.asset_base_class_name.constantize.find_by(organization_id: organization.id, object_key: object_key)
-
else
-
assets = Rails.application.config.asset_base_class_name.constantize.where(asset_tag: asset_tag)
-
if assets.count == 1
-
asset = assets.first
-
end
-
end
-
# Attempt to find the asset
-
# complain if we cant find it
-
if asset.nil?
-
add_processing_message(2, 'warning', "Could not retrieve asset with ID '#{object_key}'.")
-
@num_rows_failed += 1
-
next
-
else
-
add_processing_message(1, 'success', "Processing row[#{row}] Asset ID: '#{asset_tag}' (#{object_key})")
-
end
-
-
# Make sure this row has data otherwise skip it
-
idx = -3
-
if reader.empty?(cells.length+idx,cells.length-1)
-
@num_rows_skipped += 1
-
add_processing_message(2, 'info', "No data for row. Skipping.")
-
next
-
end
-
-
# If all the validations have passed, type the asset
-
asset = Rails.application.config.asset_base_class_name.constantize.get_typed_asset(asset)
-
-
if asset.class.to_s.include? 'Vehicle'
-
idx = -4
-
end
-
-
#---------------------------------------------------------------------
-
# Disposition
-
#---------------------------------------------------------------------
-
-
add_processing_message(2, 'success', 'Processing Disposition Report')
-
loader = DispositionUpdateEventLoader.new
-
loader.process(asset, cells[idx..-1])
-
if loader.errors?
-
row_errored = true
-
loader.errors.each { |e| add_processing_message(3, 'warning', e)}
-
end
-
if loader.warnings?
-
loader.warnings.each { |e| add_processing_message(3, 'info', e)}
-
end
-
-
# Check for any validation errors
-
event = loader.event
-
if event.valid?
-
event.upload = upload
-
event.save
-
add_processing_message(3, 'success', 'Disposition Update added.')
-
@num_rows_added += 1
-
else
-
Rails.logger.info "Disposition Update did not pass validation."
-
event.errors.full_messages.each { |e| add_processing_message(3, 'warning', e)}
-
@num_rows_failed += 1
-
end
-
end
-
end
-
-
@new_status = FileStatusType.find_by_name("Complete")
-
rescue => e
-
Rails.logger.warn "Exception caught: #{e.backtrace.join("\n")}"
-
@new_status = FileStatusType.find_by_name("Errored")
-
raise e
-
ensure
-
reader.close unless reader.nil?
-
end
-
-
add_processing_message(1, 'success', "Processing Completed at = '#{Time.now}'")
-
-
end
-
-
# Init
-
def initialize(upload)
-
super
-
@upload = upload
-
end
-
-
end
-
#------------------------------------------------------------------------------
-
#
-
# DispositionUpdatesTemplateBuilder
-
#
-
# Creates a template for capturing status updates for existing inventory
-
#
-
#------------------------------------------------------------------------------
-
class TransitDispositionUpdatesTemplateBuilder < TemplateBuilder
-
-
SHEET_NAME = DispositionUpdatesFileHandler::SHEET_NAME
-
-
protected
-
-
# Add a row for each of the asset for the org
-
def add_rows(sheet)
-
if @assets.nil?
-
asset_seed_foreign_key = @asset_class_name.constantize.asset_seed_class_name.foreign_key
-
-
assets = @asset_class_name.constantize.operational.where(organization_id: @organization.id).where(asset_seed_foreign_key => @search_parameter.id)
-
else
-
assets = @assets
-
end
-
-
assets.each do |asset|
-
next unless asset.disposable?
-
-
row_data = []
-
row_data << asset.object_key
-
row_data << asset.organization.short_name
-
row_data << asset.asset_tag
-
row_data << asset.external_id
-
row_data << asset.asset_subtype
-
row_data << asset.description
-
row_data << asset.try(:serial_number)
-
-
# Disposition report
-
row_data << nil
-
row_data << nil
-
row_data << nil
-
-
sheet.add_row row_data, :types => row_types
-
end
-
end
-
-
# Configure any other implementation specific options for the workbook
-
# such as lookup table worksheets etc.
-
def setup_workbook(workbook)
-
-
# Add a lookup table worksheet and add the lookuptable values we need to it
-
sheet = workbook.add_worksheet :name => 'lists', :state => :very_hidden
-
sheet.sheet_protection.password = 'transam'
-
-
row = []
-
@disposition_types = DispositionType.active.pluck(:name)
-
@disposition_types.each do |x|
-
row << x unless x.eql? "Unknown" or x.eql? "Transferred"
-
end
-
sheet.add_row row
-
-
end
-
-
# Performing post-processing
-
def post_process(sheet)
-
-
# protect sheet so you cannot update cells that are locked
-
sheet.sheet_protection
-
-
# Merge Cells
-
sheet.merge_cells("A1:G1")
-
-
sheet.merge_cells("H1:J1")
-
-
-
# Add data validation constraints
-
-
# This is used to get the column name of a lookup table based on its length
-
alphabet = ('A'..'Z').to_a
-
earliest_date = SystemConfig.instance.epoch
-
-
# Disposition Type
-
sheet.add_data_validation("H3:H1000", {
-
:type => :list,
-
:formula1 => "lists!$A$1:$#{alphabet[@disposition_types.size]}$1",
-
:allow_blank => true,
-
:showErrorMessage => true,
-
:errorTitle => 'Wrong input',
-
:error => 'Select a value from the list',
-
:errorStyle => :stop,
-
:showInputMessage => true,
-
:promptTitle => 'Disposition Type',
-
:prompt => 'Only values in the list are allowed'})
-
-
# Disposition Date
-
sheet.add_data_validation("I3:I1000", {
-
:type => :time,
-
:operator => :greaterThan,
-
:formula1 => earliest_date.strftime("%-m/%d/%Y"),
-
:allow_blank => true,
-
:errorTitle => 'Wrong input',
-
:error => "Date must be after #{earliest_date.strftime("%-m/%d/%Y")}",
-
:errorStyle => :stop,
-
:showInputMessage => true,
-
:promptTitle => 'Disposition Date',
-
:prompt => "Date must be after #{earliest_date.strftime("%-m/%d/%Y")}"})
-
-
-
# Sales proceeds
-
sheet.add_data_validation("J3:J1000", {
-
:type => :whole,
-
:operator => :greaterThanOrEqual,
-
:formula1 => '0',
-
:allow_blank => true,
-
:showErrorMessage => true,
-
:errorTitle => 'Wrong input',
-
:error => 'Value must be greater than 0.',
-
:errorStyle => :stop,
-
:showInputMessage => true,
-
:promptTitle => 'Sales proceeds',
-
:prompt => 'Enter a value greater than or equal to 0'})
-
-
end
-
-
# header rows
-
def header_rows
-
-
title_row = [
-
'Asset','','','','','',
-
]
-
-
title_row << ''
-
-
title_row.concat([
-
'Disposition Report',
-
'',
-
''
-
])
-
-
detail_row = [
-
'Object Key',
-
'Agency',
-
'Asset ID',
-
'External ID',
-
'Subtype',
-
'Description'
-
]
-
detail_row << 'Serial Number'
-
-
detail_row.concat([
-
# Disposition Update Columns
-
'Disposition Type',
-
'Disposition Date',
-
'Sales Proceeds'
-
])
-
-
-
[title_row, detail_row]
-
end
-
-
def column_styles
-
styles = [
-
{:name => 'asset_id_col', :column => 0},
-
{:name => 'asset_id_col', :column => 1},
-
{:name => 'asset_id_col', :column => 2},
-
{:name => 'asset_id_col', :column => 3},
-
{:name => 'asset_id_col', :column => 4},
-
{:name => 'asset_id_col', :column => 5},
-
{:name => 'asset_id_col', :column => 6},
-
]
-
-
styles.concat([
-
{:name => 'disposition_report', :column => 7},
-
{:name => 'disposition_report_date', :column => 8},
-
{:name => 'disposition_report_currency', :column => 9}
-
])
-
-
styles
-
end
-
-
def row_types
-
types = [
-
# Asset Id Block
-
:string,
-
:string,
-
:string,
-
:string,
-
:string,
-
:string,
-
]
-
types << :string
-
-
types.concat([
-
# Disposition Report Block
-
:string,
-
:integer,
-
:integer
-
])
-
-
types
-
end
-
-
# Merge the base class styles with BPT specific styles
-
def styles
-
a = []
-
a << super
-
-
a << {:name => 'asset_id_col', :bg_color => "EBF1DE", :fg_color => '000000', :b => false, :alignment => { :horizontal => :center }, :locked => true}
-
-
a << {:name => 'disposition_report', :bg_color => "F2F2F2", :alignment => { :horizontal => :center }, :locked => false }
-
a << {:name => 'disposition_report_currency', :num_fmt => 5, :bg_color => "F2F2F2", :alignment => { :horizontal => :center }, :locked => false }
-
a << {:name => 'disposition_report_integer', :num_fmt => 3, :bg_color => "F2F2F2", :alignment => { :horizontal => :center } , :locked => false }
-
a << {:name => 'disposition_report_date', :format_code => 'MM/DD/YYYY', :bg_color => "F2F2F2", :alignment => { :horizontal => :center } , :locked => false }
-
-
a.flatten
-
end
-
-
def worksheet_name
-
'Updates'
-
end
-
-
private
-
-
def initialize(*args)
-
super
-
end
-
-
def include_mileage_columns?
-
-
if @asset_class_name && (@asset_class_name.include? "Vehicle")
-
true
-
elsif @assets && (@assets.very_specific.class.to_s.include? 'Vehicle')
-
true
-
else
-
false
-
end
-
end
-
end
-
#------------------------------------------------------------------------------
-
#
-
# DispositionUpdateEventLoader
-
#
-
# Generic class for processing usage update events
-
#
-
#------------------------------------------------------------------------------
-
class EarlyDispositionRequestUpdateEventLoader < EventLoader
-
-
EXPLANATION_COL = 0
-
EVENT_DATE_COL = 1
-
SALES_PROCEEDS_COL = 2
-
MILEAGE_COL = 3
-
-
def process(asset, cells)
-
-
# Create a new DispositionUpdateEvent
-
@event = asset.build_typed_event(EarlyDispositionRequestUpdateEvent)
-
-
# Event Date
-
@event.event_date = Date.today
-
-
# Disposition Type
-
@event.comments = as_string(cells[EXPLANATION_COL])
-
-
end
-
-
private
-
def initialize
-
super
-
end
-
-
end
-
#------------------------------------------------------------------------------
-
#
-
# DispositionUpdatesFileHandler
-
#
-
# Generic class for processing disposition updates for assets from a spreadsheet.
-
#
-
# This processes a file that has been exported as a template from the TransAM
-
# application
-
#
-
#------------------------------------------------------------------------------
-
class EarlyDispositionRequestUpdatesFileHandler < AbstractFileHandler
-
-
OBJECT_KEY_COL = 0
-
ASSET_SUBTYPE_COL = 2
-
ASSET_TAG_COL = 3
-
-
NUM_HEADER_ROWS = 2
-
SHEET_NAME = "Updates"
-
-
# Perform the processing
-
def process(upload)
-
-
@num_rows_processed = 0
-
@num_rows_added = 0
-
@num_rows_skipped = 0
-
@num_rows_replaced = 0
-
@num_rows_failed = 0
-
-
# Get the pertinent info from the upload record
-
file_url = upload.file.url # Usually stored on S3
-
organization = upload.organization # Organization who owns the assets
-
system_user = User.find(1) # System user is always the first user
-
-
add_processing_message(1, 'success', "Updating asset early disposition requests from '#{upload.original_filename}'")
-
add_processing_message(1, 'success', "Start time = '#{Time.now}'")
-
-
# Open the spreadsheet and start to process the asset events
-
begin
-
-
reader = SpreadsheetReader.new(file_url)
-
reader.open(SHEET_NAME)
-
-
Rails.logger.info " File Opened."
-
Rails.logger.info " Num Rows = #{reader.num_rows}, Num Cols = #{reader.num_cols}, Num Header Rows = #{NUM_HEADER_ROWS}"
-
-
# Process each row
-
count_blank_rows = 0
-
first_row = NUM_HEADER_ROWS + 1
-
first_row.upto(reader.last_row) do |row|
-
# Read the next row from the spreadsheet
-
cells = reader.read(row)
-
if reader.empty_row?
-
count_blank_rows += 1
-
if count_blank_rows > 10
-
break
-
end
-
else
-
notes = []
-
count_blank_rows = 0
-
@num_rows_processed += 1
-
-
# Get the asset by the object key
-
object_key = cells[OBJECT_KEY_COL].to_s
-
subtype_str = cells[ASSET_SUBTYPE_COL].to_s
-
# asset tags are sometimes stored as numbers
-
asset_tag = cells[ASSET_TAG_COL].to_s
-
-
Rails.logger.debug " Processing row #{row}. Asset ID = '#{object_key}', Subtype = '#{subtype_str}', Asset Tag = '#{asset_tag}'"
-
-
if object_key.present?
-
asset = Asset.find_by('organization_id = ? AND object_key = ?', organization.id, object_key)
-
else
-
assets = Asset.where('asset_tag = ?', asset_tag)
-
if assets.count == 1
-
asset = assets.first
-
end
-
end
-
# Attempt to find the asset
-
# complain if we cant find it
-
if asset.nil?
-
add_processing_message(2, 'warning', "Could not retrieve asset with ID '#{object_key}'.")
-
@num_rows_failed += 1
-
next
-
else
-
add_processing_message(1, 'success', "Processing row[#{row}] Asset ID: '#{object_key}', Subtype: '#{subtype_str}', Asset Tag: '#{asset_tag}'")
-
end
-
-
#### Validations on Asset ####
-
# Check to see if this asset tag and subtype are the same
-
unless asset.asset_subtype.name == subtype_str
-
add_processing_message(2, 'warning', "Mismatch on asset subtype. Found subtype '#{subtype_str}' expected '#{asset.asset_subtype.name}'. Skipping row.")
-
@num_rows_failed += 1
-
next
-
end
-
-
# Check to see if this asset tag and subtype are the same
-
unless asset.asset_tag == asset_tag
-
add_processing_message(2, 'warning', "Mismatch on asset tag. Found tag '#{asset_tag}' expected '#{asset.asset_tag}'. Skipping row.")
-
@num_rows_failed += 1
-
next
-
end
-
-
# Make sure this row has data otherwise skip it
-
if reader.empty?(6,9)
-
@num_rows_skipped += 1
-
add_processing_message(2, 'info', "No data for row. Skipping.")
-
next
-
end
-
-
# If all the validations have passed, type the asset
-
asset = Asset.get_typed_asset(asset)
-
-
#---------------------------------------------------------------------
-
# Disposition
-
#---------------------------------------------------------------------
-
unless reader.empty?(6,6)
-
add_processing_message(2, 'success', 'Processing Disposition Request')
-
loader = EarlyDispositionRequestUpdateEventLoader.new
-
loader.process(asset, [cells[6]])
-
if loader.errors?
-
row_errored = true
-
loader.errors.each { |e| add_processing_message(3, 'warning', e)}
-
end
-
if loader.warnings?
-
loader.warnings.each { |e| add_processing_message(3, 'info', e)}
-
end
-
-
# Check for any validation errors
-
event = loader.event
-
if event.valid?
-
event.upload = upload
-
event.save
-
add_processing_message(3, 'success', 'Disposition Request added.')
-
@num_rows_added += 1
-
else
-
Rails.logger.info "Disposition Request did not pass validation."
-
event.errors.full_messages.each { |e| add_processing_message(3, 'warning', e)}
-
@num_rows_failed += 1
-
end
-
end
-
end
-
end
-
-
@new_status = FileStatusType.find_by_name("Complete")
-
rescue => e
-
Rails.logger.warn "Exception caught: #{e.backtrace.join("\n")}"
-
@new_status = FileStatusType.find_by_name("Errored")
-
raise e
-
ensure
-
reader.close unless reader.nil?
-
end
-
-
add_processing_message(1, 'success', "Processing Completed at = '#{Time.now}'")
-
-
end
-
-
# Init
-
def initialize(upload)
-
super
-
@upload = upload
-
end
-
-
end
-
#------------------------------------------------------------------------------
-
#
-
# EventLoader
-
#
-
# Abstract base class for a loader that process events.
-
#
-
#------------------------------------------------------------------------------
-
class EventLoader < InventoryLoader
-
-
def event
-
@event
-
end
-
-
protected
-
-
private
-
-
def initialize
-
super
-
@event = nil
-
end
-
-
end
-
#------------------------------------------------------------------------------
-
#
-
# InventoryLoader
-
#
-
# Abstract base class for a loader. Provides errors, warnings, lists, coercion
-
# methods etc. All spreadsheet loaders should derive from this.
-
#
-
#------------------------------------------------------------------------------
-
class InventoryLoader
-
-
attr_accessor :upload
-
-
def comments?
-
@comments.count > 0
-
end
-
-
def warnings?
-
@warnings.count > 0
-
end
-
def errors?
-
@errors.count > 0
-
end
-
def comments
-
@comments
-
end
-
-
def warnings
-
@warnings
-
end
-
-
def errors
-
@errors
-
end
-
-
protected
-
-
def is_number?(val)
-
Float(val) != nil rescue false
-
end
-
-
def as_boolean(cell_val)
-
if cell_val.blank?
-
false
-
else
-
(cell_val.to_s.start_with?("Y", "y", "T", "t"))
-
end
-
end
-
-
def as_year(cell_val)
-
#Rails.logger.debug "Converting to year '#{cell_val}'"
-
val = as_integer(cell_val)
-
if val < 50
-
val += 2000
-
end
-
val
-
end
-
-
# Roo parses date and datetime formatted cells to the corresponding class
-
# so we check to see if they are already processed otherwise we attempt
-
# to parse the string value
-
def as_date(cell_val)
-
# Check to see if the spreadsheet encoded this as a number
-
if cell_val.is_a?(Date)
-
val = cell_val
-
elsif
-
cell_val.is_a?(DateTime)
-
val = cell_val.date
-
else
-
# parse the string as a date
-
val = coerce_date(cell_val.to_s)
-
end
-
val
-
end
-
-
def as_float(cell_val)
-
if is_number?(cell_val)
-
val = cell_val.to_f
-
else
-
val = 0.0
-
end
-
val
-
end
-
-
def as_integer(cell_val)
-
val = as_float(cell_val) + 0.5
-
val.to_i
-
end
-
-
def as_string(cell_val)
-
# Check to see if the spreadsheet encoded this as a number
-
#if cell_val.is_a?(Float)
-
# convert it first to an int to remove the decimal point then to the string
-
# val = cell_val.to_i.to_s
-
#else
-
val = cell_val.to_s
-
#end
-
-
val
-
end
-
-
# Return the manufacturer for the asset, if not found, Unknown is returned
-
def get_manufacturer(manufacturer_str, asset)
-
# See if we can get a correct hit
-
result = Manufacturer.where('filter = ? AND (code = ? OR name = ?)', asset.class.name, manufacturer_str, manufacturer_str).first
-
# Nothing so try matching the start of the name
-
if result.nil?
-
result = Manufacturer.where('filter = ? AND name LIKE ?', asset.class.name, "#{manufacturer_str}%").first
-
end
-
# Still nothing! Default to class unknown
-
if result.nil?
-
result = Manufacturer.where('filter = ? AND name = ?', asset.class.name, "Unknown").first
-
end
-
result
-
end
-
-
protected
-
-
# Attempts to coerce a string containing an excel formatted date back into a
-
# Ruby date
-
def coerce_date(val)
-
#puts "coerce_date: '#{val}'"
-
# If there is no date, dont try to set one
-
if val.blank?
-
return nil
-
end
-
-
#strip date first
-
val.strip!
-
-
# Pull the date apart and try to determine what is there
-
elems = val.split('/')
-
# check for just a year and set it to the first day of the fiscal year
-
if elems.size == 1
-
date_str = "#{SystemConfig.instance.start_of_fiscal_year}-#{elems[0]}"
-
d = Date.strptime(date_str, "%m-%d-%Y")
-
elsif elems.last.length == 2
-
d = Date.strptime(val, "%m/%d/%y")
-
else
-
d = Date.strptime(val, "%m/%d/%Y")
-
end
-
#puts d
-
d
-
end
-
-
private
-
-
def initialize
-
@warnings = []
-
@errors = []
-
@comments = []
-
end
-
-
end
-
#------------------------------------------------------------------------------
-
#
-
# InventoryUpdatesFileHandler
-
#
-
# Generic class for processing inventory updates for assets from a spreadsheet.
-
#
-
# This processes a file that has been exported as a template from the TransAM
-
# application
-
#
-
#------------------------------------------------------------------------------
-
class InventoryUpdatesFileHandler < AbstractFileHandler
-
-
OBJECT_KEY_COL = 0
-
ASSET_SUBTYPE_COL = 2
-
ASSET_TAG_COL = 3
-
-
NUM_HEADER_ROWS = 2
-
SHEET_NAME = "Updates"
-
-
# Perform the processing
-
def process(upload)
-
-
@num_rows_processed = 0
-
@num_rows_added = 0
-
@num_rows_skipped = 0
-
@num_rows_replaced = 0
-
@num_rows_failed = 0
-
-
# Get the pertinent info from the upload record
-
file_url = upload.file.url # Usually stored on S3
-
organization = upload.organization # Organization who owns the assets
-
system_user = User.find(1) # System user is always the first user
-
-
add_processing_message(1, 'success', "Updating asset status from '#{upload.original_filename}'")
-
add_processing_message(1, 'success', "Start time = '#{Time.now}'")
-
-
# Open the spreadsheet and start to process the asset events
-
begin
-
-
reader = SpreadsheetReader.new(file_url)
-
reader.open(SHEET_NAME)
-
-
Rails.logger.info " File Opened."
-
Rails.logger.info " Num Rows = #{reader.num_rows}, Num Cols = #{reader.num_cols}, Num Header Rows = #{NUM_HEADER_ROWS}"
-
-
# Process each row
-
count_blank_rows = 0
-
first_row = NUM_HEADER_ROWS + 1
-
first_row.upto(reader.last_row) do |row|
-
# Read the next row from the spreadsheet
-
cells = reader.read(row)
-
if reader.empty_row?
-
count_blank_rows += 1
-
if count_blank_rows > 10
-
break
-
end
-
else
-
notes = []
-
count_blank_rows = 0
-
@num_rows_processed += 1
-
-
# Get the asset by the object key
-
object_key = cells[OBJECT_KEY_COL].to_s
-
subtype_str = cells[ASSET_SUBTYPE_COL].to_s
-
# asset tags are sometimes stored as numbers
-
asset_tag = cells[ASSET_TAG_COL].to_s
-
-
Rails.logger.debug " Processing row #{row}. Asset ID = '#{object_key}', Subtype = '#{subtype_str}', Asset Tag = '#{asset_tag}'"
-
asset = Asset.find_by('organization_id = ? AND object_key = ?', organization.id, object_key)
-
-
# Attempt to find the asset
-
# complain if we cant find it
-
if asset.nil?
-
add_processing_message(2, 'warning', "Could not retrieve asset with ID '#{object_key}'.")
-
@num_rows_failed += 1
-
next
-
else
-
add_processing_message(1, 'success', "Processing row[#{row}] Asset ID: '#{object_key}', Subtype: '#{subtype_str}', Asset Tag: '#{asset_tag}'")
-
end
-
-
#### Validations on Asset ####
-
# Check to see if this asset tag and subtype are the same
-
unless asset.asset_subtype.name == subtype_str
-
add_processing_message(2, 'warning', "Mismatch on asset subtype. Found subtype '#{subtype_str}' expected '#{asset.asset_subtype.name}'. Skipping row.")
-
@num_rows_failed += 1
-
next
-
end
-
-
# Make sure this row has data otherwise skip it
-
if reader.empty?(8,8) and reader.empty?(12,12) and reader.empty?(16,16)
-
@num_rows_skipped += 1
-
add_processing_message(2, 'info', "No data for row. Skipping.")
-
next
-
end
-
-
has_new_event = false
-
-
# If all the validations have passed, type the asset
-
asset = Asset.get_typed_asset(asset)
-
-
#---------------------------------------------------------------------
-
# Service Status
-
#---------------------------------------------------------------------
-
unless reader.empty?(8, 8)
-
add_processing_message(2, 'success', 'Processing Service Status Report')
-
loader = ServiceStatusUpdateEventLoader.new
-
loader.process(asset, cells[8..9])
-
if loader.errors?
-
row_errored = true
-
loader.errors.each { |e| add_processing_message(3, 'warning', e)}
-
end
-
if loader.warnings?
-
loader.warnings.each { |e| add_processing_message(3, 'info', e)}
-
end
-
-
# Check for any validation errors
-
event = loader.event
-
if event.valid?
-
event.upload = upload
-
event.save
-
add_processing_message(3, 'success', 'Service Status updated.')
-
has_new_event = true
-
else
-
Rails.logger.info "Service Status did not pass validation."
-
event.errors.full_messages.each { |e| add_processing_message(3, 'warning', e)}
-
end
-
end
-
-
#---------------------------------------------------------------------
-
# Condition
-
#---------------------------------------------------------------------
-
unless reader.empty?(12, 12)
-
add_processing_message(2, 'success', 'Processing Condition Report')
-
loader = ConditionUpdateEventLoader.new
-
loader.process(asset, cells[12..13])
-
if loader.errors?
-
row_errored = true
-
loader.errors.each { |e| add_processing_message(3, 'warning', e)}
-
end
-
if loader.warnings?
-
loader.warnings.each { |e| add_processing_message(3, 'info', e)}
-
end
-
-
# Check for any validation errors
-
event = loader.event
-
if event.valid?
-
event.upload = upload
-
event.save
-
add_processing_message(3, 'success', 'Condition Update added.')
-
has_new_event = true
-
else
-
Rails.logger.info "Condition Update did not pass validation."
-
event.errors.full_messages.each { |e| add_processing_message(3, 'warning', e)}
-
end
-
end
-
-
#---------------------------------------------------------------------
-
# Fire update events for the asset if a new event was added
-
#---------------------------------------------------------------------
-
if has_new_event
-
@num_rows_added += 1
-
#Delayed::Job.enqueue AssetUpdateJob.new(asset.object_key), :priority => 10, :run_at => 30.seconds.from_now
-
end
-
end
-
end
-
-
@new_status = FileStatusType.find_by_name("Complete")
-
rescue => e
-
Rails.logger.warn "Exception caught: #{e.backtrace.join("\n")}"
-
@new_status = FileStatusType.find_by_name("Errored")
-
raise e
-
ensure
-
reader.close unless reader.nil?
-
end
-
-
add_processing_message(1, 'success', "Processing Completed at = '#{Time.now}'")
-
-
end
-
-
# Init
-
def initialize(upload)
-
super
-
@upload = upload
-
end
-
-
end
-
#-------------------------------------------------------------------------------
-
#
-
# TransitInventoryUpdatesTemplateBuilder
-
#
-
# Creates a template for capturing status updates for existing transit inventory
-
#
-
#-------------------------------------------------------------------------------
-
class InventoryUpdatesTemplateBuilder < TemplateBuilder
-
-
SHEET_NAME = InventoryUpdatesFileHandler::SHEET_NAME
-
-
protected
-
-
# Add a row for each of the asset for the org
-
def add_rows(sheet)
-
-
if @assets.nil?
-
asset_seed_foreign_key = @asset_class_name.constantize.asset_seed_class_name.foreign_key
-
-
assets = @asset_class_name.constantize.operational.where(organization_id: @organization.id).where(asset_seed_foreign_key => @search_parameter.id)
-
else
-
assets = @assets
-
end
-
-
assets.each do |asset|
-
row_data = []
-
row_data << asset.object_key
-
row_data << asset.organization.short_name
-
row_data << asset.asset_tag
-
row_data << asset.external_id
-
row_data << asset.asset_subtype
-
row_data << asset.description
-
row_data << asset.try(:serial_number)
-
-
row_data << asset.try(:service_status_type).try(:name) #prev_service_status
-
row_data << asset.service_status_updates.last.try(:event_date) # prev_service_status date
-
row_data << nil # current_service_status
-
row_data << nil # date
-
-
row_data << asset.reported_condition_rating.to_s # Previous Condition
-
row_data << asset.reported_condition_date # Previous Condition
-
row_data << nil # Current Condition
-
row_data << nil # Date
-
-
sheet.add_row row_data
-
end
-
end
-
-
# Configure any other implementation specific options for the workbook
-
# such as lookup table worksheets etc.
-
def setup_workbook(workbook)
-
-
# Add a lookup table worksheet and add the lookuptable values we need to it
-
sheet = workbook.add_worksheet :name => 'lists', :state => :very_hidden
-
sheet.sheet_protection.password = 'transam'
-
-
row = []
-
@service_types = ServiceStatusType.active.pluck(:name)
-
@service_types.each do |x|
-
row << x unless x.eql? "Unknown"
-
end
-
sheet.add_row row
-
-
end
-
-
# Performing post-processing
-
def post_process(sheet)
-
-
# protect sheet so you cannot update cells that are locked
-
sheet.sheet_protection
-
-
# Merge Cells?
-
sheet.merge_cells("A1:H1")
-
sheet.merge_cells("I1:L1")
-
sheet.merge_cells("M1:P1")
-
-
# This is used to get the column name of a lookup table based on its length
-
alphabet = ('A'..'Z').to_a
-
earliest_date = SystemConfig.instance.epoch
-
-
# Service Status
-
sheet.add_data_validation("K3:K1000", {
-
:type => :list,
-
:formula1 => "lists!$A$1:$#{alphabet[@service_types.size]}$1",
-
:allow_blank => true,
-
:showErrorMessage => true,
-
:errorTitle => 'Wrong input',
-
:error => 'Select a value from the list',
-
:errorStyle => :stop,
-
:showInputMessage => true,
-
:promptTitle => 'Service type',
-
:prompt => 'Only values in the list are allowed'})
-
-
# Service Status Date
-
sheet.add_data_validation("L3:L1000", {
-
:type => :time,
-
:operator => :greaterThan,
-
:formula1 => earliest_date.strftime("%-m/%d/%Y"),
-
:allow_blank => true,
-
:errorTitle => 'Wrong input',
-
:error => "Date must be after #{earliest_date.strftime("%-m/%d/%Y")}",
-
:errorStyle => :stop,
-
:showInputMessage => true,
-
:promptTitle => 'Status Reporting Date',
-
:prompt => "Date must be after #{earliest_date.strftime("%-m/%d/%Y")}"})
-
-
# Condition Rating > 1 - 5, real number
-
sheet.add_data_validation("O2:O1000", {
-
:type => :decimal,
-
:operator => :between,
-
:formula1 => '1.0',
-
:formula2 => '5.0',
-
:allow_blank => true,
-
:showErrorMessage => true,
-
:errorTitle => 'Wrong input',
-
:error => 'Rating value must be between 1 and 5',
-
:errorStyle => :stop,
-
:showInputMessage => true,
-
:promptTitle => 'Condition Rating',
-
:prompt => 'Only values between 1 and 5'})
-
-
# Condition date
-
sheet.add_data_validation("P2:P1000", {
-
:type => :whole,
-
:operator => :greaterThanOrEqual,
-
:formula1 => earliest_date.strftime("%-m/%d/%Y"),
-
:allow_blank => true,
-
:showErrorMessage => true,
-
:errorTitle => 'Wrong input',
-
:error => "Date must be after #{earliest_date.strftime("%-m/%d/%Y")}",
-
:errorStyle => :stop,
-
:showInputMessage => true,
-
:promptTitle => 'Reporting Date',
-
:prompt => "Date must be after #{earliest_date.strftime("%-m/%d/%Y")}"})
-
end
-
-
# header rows
-
def header_rows
-
title_row = [
-
'Asset','','','','','',
-
]
-
title_row << ''
-
-
title_row.concat([
-
'',
-
'Service Status Report',
-
'',
-
'',
-
'',
-
'Condition Report',
-
'',
-
'',
-
''
-
])
-
-
detail_row = [
-
'Object Key',
-
'Agency',
-
'Asset ID',
-
'External ID',
-
'Subtype',
-
'Description'
-
]
-
-
detail_row.concat([
-
# Status Report Columns
-
'Current Status',
-
'Reporting Date',
-
'New Status',
-
'Reporting Date',
-
-
# Condition Report Columns
-
'Current Condition',
-
'Reporting Date',
-
'New Condition',
-
'Reporting Date'
-
])
-
-
[title_row, detail_row]
-
end
-
-
def column_styles
-
styles = [
-
{:name => 'asset_id_col', :column => 0},
-
{:name => 'asset_id_col', :column => 1},
-
{:name => 'asset_id_col', :column => 2},
-
{:name => 'asset_id_col', :column => 3},
-
{:name => 'asset_id_col', :column => 4},
-
{:name => 'asset_id_col', :column => 5},
-
{:name => 'asset_id_col', :column => 6},
-
-
]
-
-
styles << {:name => 'asset_id_col', :column => 7}
-
diff = 0
-
-
styles.concat([
-
{:name => 'service_status_string_locked', :column => 8},
-
{:name => 'service_status_date_locked', :column => 9},
-
{:name => 'service_status_string', :column => 10},
-
{:name => 'service_status_date', :column => 11},
-
-
{:name => 'condition_float_locked', :column => 12},
-
{:name => 'condition_date_locked', :column => 13},
-
{:name => 'condition_float', :column => 14},
-
{:name => 'condition_date', :column => 15}
-
])
-
styles
-
end
-
-
def row_types
-
types = [
-
# Asset Id Block
-
:string,
-
:string,
-
:string,
-
:string,
-
:string,
-
:string,
-
]
-
types << :string
-
-
types.concat([
-
# Service Status Report Block
-
:string,
-
:date,
-
:string,
-
:date,
-
-
# Condition Report Block
-
:float,
-
:date,
-
:float,
-
:date
-
])
-
types
-
end
-
# Merge the base class styles with BPT specific styles
-
def styles
-
a = []
-
a << super
-
-
# Header Styles
-
a << {:name => 'asset_id_col', :bg_color => "EBF1DE", :fg_color => '000000', :b => false, :alignment => { :horizontal => :left }}
-
-
a << {:name => 'service_status_string_locked', :bg_color => "F2DCDB", :alignment => { :horizontal => :center } , :locked => true }
-
a << {:name => 'service_status_date_locked', :format_code => 'MM/DD/YYYY', :bg_color => "F2DCDB", :alignment => { :horizontal => :center } , :locked => true }
-
a << {:name => 'service_status_string', :bg_color => "F2DCDB", :alignment => { :horizontal => :center } , :locked => false }
-
a << {:name => 'service_status_date', :format_code => 'MM/DD/YYYY', :bg_color => "F2DCDB", :alignment => { :horizontal => :center } , :locked => false }
-
-
a << {:name => 'condition_float_locked', :num_fmt => 2, :bg_color => "DDD9C4", :alignment => { :horizontal => :center } , :locked => true }
-
a << {:name => 'condition_date_locked', :format_code => 'MM/DD/YYYY', :bg_color => "DDD9C4", :alignment => { :horizontal => :center } , :locked => true }
-
a << {:name => 'condition_float', :num_fmt => 2, :bg_color => "DDD9C4", :alignment => { :horizontal => :center } , :locked => false }
-
a << {:name => 'condition_date', :format_code => 'MM/DD/YYYY', :bg_color => "DDD9C4", :alignment => { :horizontal => :center } , :locked => false }
-
-
a.flatten
-
end
-
-
def worksheet_name
-
'Updates'
-
end
-
-
private
-
-
def initialize(*args)
-
super
-
end
-
-
def include_mileage_columns?
-
-
if @asset_class_name && (@asset_class_name.include? "Vehicle")
-
true
-
elsif @assets && (@assets.very_specific.class.to_s.include? 'Vehicle')
-
true
-
else
-
false
-
end
-
end
-
-
-
end
-
#------------------------------------------------------------------------------
-
# MaintenanceUpdatesEventLoader
-
#
-
# Registers a maintenance update event against an asset
-
#
-
#------------------------------------------------------------------------------
-
class MaintenanceUpdatesEventLoader < EventLoader
-
-
MAINTENANCE_TYPE_COL = 0
-
MILEAGE_COL = 2
-
EVENT_DATE_COL = 1
-
NOTES_COL = 3
-
-
def process(asset, cells)
-
-
# Create a new ServiceStatusUpdateEvent
-
@event = asset.build_typed_event(MaintenanceUpdateEvent)
-
-
# Maintenance Type
-
@event.maintenance_type = MaintenanceType.search(cells[MAINTENANCE_TYPE_COL])
-
if @event.maintenance_type.nil?
-
@errors << "Could not find maintenance type '#{cells[MAINTENANCE_TYPE_COL]}'."
-
end
-
-
# Mileage
-
mileage = as_integer(cells[MILEAGE_COL])
-
@event.current_mileage = mileage if mileage.to_i > 0
-
-
# Event Date
-
@event.event_date = as_date(cells[EVENT_DATE_COL])
-
# Notes
-
if asset.class.to_s.include? 'Vehicle'
-
notes = as_string(cells[NOTES_COL])
-
else
-
notes = as_string(cells[NOTES_COL-1])
-
end
-
@event.comments = notes unless notes.blank?
-
-
end
-
-
private
-
def initialize
-
super
-
end
-
-
end
-
#------------------------------------------------------------------------------
-
#
-
# MaintenanceUpdatesFileHandler
-
#
-
# Generic class for processing maintenance updates for assets from a spreadsheet.
-
#
-
# This processes a file that has been exported as a template from the TransAM
-
# application
-
#
-
#------------------------------------------------------------------------------
-
class MaintenanceUpdatesFileHandler < AbstractFileHandler
-
-
OBJECT_KEY_COL = 0
-
ASSET_TAG_COL = 2
-
-
NUM_HEADER_ROWS = 2
-
SHEET_NAME = "Updates"
-
-
# Perform the processing
-
def process(upload)
-
-
@num_rows_processed = 0
-
@num_rows_added = 0
-
@num_rows_skipped = 0
-
@num_rows_replaced = 0
-
@num_rows_failed = 0
-
-
# Get the pertinent info from the upload record
-
file_url = upload.file.url # Usually stored on S3
-
organization = upload.organization # Organization who owns the assets
-
system_user = User.find(1) # System user is always the first user
-
-
add_processing_message(1, 'success', "Updating asset maintenance history from '#{upload.original_filename}'")
-
add_processing_message(1, 'success', "Start time = '#{Time.now}'")
-
-
# Open the spreadsheet and start to process the asset events
-
begin
-
-
reader = SpreadsheetReader.new(file_url)
-
reader.open(SHEET_NAME)
-
-
Rails.logger.info " File Opened."
-
Rails.logger.info " Num Rows = #{reader.num_rows}, Num Cols = #{reader.num_cols}, Num Header Rows = #{NUM_HEADER_ROWS}"
-
-
# Process each row
-
count_blank_rows = 0
-
first_row = NUM_HEADER_ROWS + 1
-
first_row.upto(reader.last_row) do |row|
-
# Read the next row from the spreadsheet
-
cells = reader.read(row)
-
if reader.empty_row?
-
count_blank_rows += 1
-
if count_blank_rows > 10
-
break
-
end
-
else
-
notes = []
-
count_blank_rows = 0
-
@num_rows_processed += 1
-
-
# Get the asset by the object key
-
object_key = cells[OBJECT_KEY_COL].to_s
-
# asset tags are sometimes stored as numbers
-
asset_tag = cells[ASSET_TAG_COL].to_s
-
-
Rails.logger.debug " Processing row #{row}. Asset ID = '#{object_key}', Asset Tag = '#{asset_tag}'"
-
if object_key.present?
-
asset = Rails.application.config.asset_base_class_name.constantize.find_by(organization_id: organization.id, object_key: object_key)
-
else
-
assets = Rails.application.config.asset_base_class_name.constantize.where(asset_tag: asset_tag)
-
if assets.count == 1
-
asset = assets.first
-
end
-
end
-
-
# Attempt to find the asset
-
# complain if we cant find it
-
if asset.nil?
-
add_processing_message(2, 'warning', "Could not retrieve asset with ID '#{object_key}'.")
-
@num_rows_failed += 1
-
next
-
else
-
add_processing_message(1, 'success', "Processing row[#{row}] Asset ID: '#{asset_tag}' (#{object_key})")
-
end
-
-
# Make sure this row has data otherwise skip it
-
idx = -3 # always assume last 3 cells, sometimes 4 with mileage but 3 always includes
-
if reader.empty?(cells.length+idx,cells.length-1)
-
@num_rows_skipped += 1
-
add_processing_message(2, 'info', "No data for row. Skipping.")
-
next
-
end
-
-
# If all the validations have passed, type the asset
-
asset = Rails.application.config.asset_base_class_name.constantize.get_typed_asset(asset)
-
-
if asset.class.to_s.include? 'Vehicle'
-
idx = -4
-
end
-
-
#---------------------------------------------------------------------
-
# Maintenance Update
-
#---------------------------------------------------------------------
-
add_processing_message(2, 'success', 'Processing Maintenance Report')
-
loader = MaintenanceUpdatesEventLoader.new
-
loader.process(asset, cells[idx..-1])
-
if loader.errors?
-
row_errored = true
-
loader.errors.each { |e| add_processing_message(3, 'warning', e)}
-
end
-
if loader.warnings?
-
loader.warnings.each { |e| add_processing_message(3, 'info', e)}
-
end
-
-
# Check for any validation errors
-
event = loader.event
-
if event.valid?
-
event.upload = upload
-
event.save
-
add_processing_message(3, 'success', 'Maintenance history updated.')
-
#Delayed::Job.enqueue AssetMaintenanceUpdateJob.new(asset.object_key), :priority => 10
-
else
-
Rails.logger.info "Maintenance record did not pass validation."
-
event.errors.full_messages.each { |e| add_processing_message(3, 'warning', e)}
-
end
-
end
-
end
-
-
@new_status = FileStatusType.find_by_name("Complete")
-
rescue => e
-
Rails.logger.warn "Exception caught: #{e.backtrace.join("\n")}"
-
@new_status = FileStatusType.find_by_name("Errored")
-
raise e
-
ensure
-
reader.close unless reader.nil?
-
end
-
-
add_processing_message(1, 'success', "Processing Completed at = '#{Time.now}'")
-
-
end
-
-
# Init
-
def initialize(upload)
-
super
-
@upload = upload
-
end
-
-
def included_serial_number?(asset)
-
asset.type_of? :vehicle or asset.type_of? :support_vehicle or asset.type_of? :equipment
-
end
-
-
end
-
#------------------------------------------------------------------------------
-
#
-
# MaintenanceUpdatesTemplateBuilder
-
#
-
# Creates a template for capturing maintenance updates for existing inventory
-
#
-
#------------------------------------------------------------------------------
-
class MaintenanceUpdatesTemplateBuilder < TemplateBuilder
-
-
SHEET_NAME = MaintenanceUpdatesFileHandler::SHEET_NAME
-
-
protected
-
-
# Add a row for each of the asset for the org
-
def add_rows(sheet)
-
if @assets.nil?
-
asset_seed_foreign_key = @asset_class_name.constantize.asset_seed_class_name.foreign_key
-
-
assets = @asset_class_name.constantize.operational.where(organization_id: @organization.id).where(asset_seed_foreign_key => @search_parameter.id)
-
else
-
assets = @assets
-
end
-
-
assets.each do |a|
-
-
asset = Rails.application.config.asset_base_class_name.constantize.get_typed_asset(a)
-
row_data = []
-
row_data << asset.object_key
-
row_data << asset.organization.short_name
-
row_data << asset.asset_tag
-
row_data << asset.external_id
-
row_data << asset.asset_subtype
-
row_data << asset.description
-
row_data << asset.try(:serial_number)
-
-
if asset.respond_to? :maintenance_updates and asset.maintenance_updates.present?
-
event = asset.maintenance_updates.last
-
row_data << event.maintenance_type.name
-
row_data << event.event_date
-
row_data << event.current_mileage if include_mileage_columns?
-
else
-
row_data << nil # current_maintenance type
-
row_data << nil # reprot date
-
row_data << nil if include_mileage_columns?
-
end
-
row_data << nil # current_maintenance type
-
row_data << nil # report date
-
row_data << nil if include_mileage_columns? # current mileage
-
row_data << nil # notes
-
-
sheet.add_row row_data
-
end
-
end
-
-
# Configure any other implementation specific options for the workbook
-
# such as lookup table worksheets etc.
-
def setup_workbook(workbook)
-
-
# Add a lookup table worksheet and add the lookuptable values we need to it
-
sheet = workbook.add_worksheet :name => 'lists', :state => :very_hidden
-
sheet.sheet_protection.password = 'transam'
-
-
row = []
-
@maintenance_types = MaintenanceType.active.pluck(:name)
-
@maintenance_types.each do |x|
-
row << x unless x.eql? "Unknown"
-
end
-
sheet.add_row row
-
-
end
-
-
# Performing post-processing
-
def post_process(sheet)
-
-
# protect sheet so you cannot update cells that are locked
-
sheet.sheet_protection
-
-
# Merge Cells
-
sheet.merge_cells("A1:G1")
-
if include_mileage_columns?
-
sheet.merge_cells("H1:N1")
-
else
-
sheet.merge_cells("H1:L1")
-
end
-
-
-
# This is used to get the column name of a lookup table based on its length
-
alphabet = ('A'..'Z').to_a
-
earliest_date = SystemConfig.instance.epoch
-
-
# Maintenance Type
-
sheet.add_data_validation((include_mileage_columns?) ? "K3:K1000": "J3:J1000", {
-
:type => :list,
-
:formula1 => "lists!$A$1:$#{alphabet[@maintenance_types.size]}$1",
-
:allow_blank => true,
-
:showErrorMessage => true,
-
:errorTitle => 'Wrong input',
-
:error => 'Select a value from the list',
-
:errorStyle => :stop,
-
:showInputMessage => true,
-
:promptTitle => 'Maintenance type',
-
:prompt => 'Only values in the list are allowed'})
-
-
# Maintenance Date
-
sheet.add_data_validation((include_mileage_columns?) ? "L3:L1000": "K1:K1000", {
-
:type => :time,
-
:operator => :greaterThan,
-
:formula1 => earliest_date.strftime("%-m/%d/%Y"),
-
:allow_blank => true,
-
:errorTitle => 'Wrong input',
-
:error => "Date must be after #{earliest_date.strftime("%-m/%d/%Y")}",
-
:errorStyle => :stop,
-
:showInputMessage => true,
-
:promptTitle => 'Maintenance Date',
-
:prompt => "Date must be after #{earliest_date.strftime("%-m/%d/%Y")}"})
-
-
if include_mileage_columns?
-
# Milage -Integer > 0
-
sheet.add_data_validation("M3:M1000", {
-
:type => :whole,
-
:operator => :greaterThan,
-
:allow_blank => true,
-
:showErrorMessage => true,
-
:errorTitle => 'Wrong input',
-
:error => 'Milage must be > 0',
-
:errorStyle => :stop,
-
:showInputMessage => true,
-
:promptTitle => 'Current mileage',
-
:prompt => 'Only values greater than 0'})
-
end
-
-
-
end
-
-
# header rows
-
def header_rows
-
title_row = [
-
'Asset','','','','','','',
-
]
-
-
title_row.concat([
-
'Maintenance Report',
-
'',
-
'',
-
'',
-
'',
-
])
-
-
if include_mileage_columns?
-
title_row.concat([
-
'',
-
''
-
])
-
end
-
-
detail_row = [
-
'Object Key',
-
'Agency',
-
'Asset ID',
-
'External ID',
-
'Subtype',
-
'Description'
-
]
-
-
detail_row << 'Serial Number'
-
-
if include_mileage_columns?
-
detail_row.concat([
-
# Maintenance Report Columns
-
'Last Maintenance',
-
'Last Maintenance Date',
-
'Last Mileage',
-
'Maintenance Performed',
-
'Maintenance Date',
-
'Mileage',
-
'Notes'
-
])
-
else
-
detail_row.concat([
-
# Maintenance Report Columns
-
'Last Maintenance',
-
'Last Maintenance Date',
-
'Maintenance Performed',
-
'Maintenance Date',
-
'Notes'
-
])
-
end
-
-
[title_row, detail_row]
-
end
-
-
def column_styles
-
styles = [
-
{:name => 'asset_id_col', :column => 0},
-
{:name => 'asset_id_col', :column => 1},
-
{:name => 'asset_id_col', :column => 2},
-
{:name => 'asset_id_col', :column => 3},
-
{:name => 'asset_id_col', :column => 4},
-
{:name => 'asset_id_col', :column => 5},
-
{:name => 'asset_id_col', :column => 6},
-
]
-
-
if include_mileage_columns?
-
styles.concat([
-
{:name => 'maintenance_type_locked', :column => 7},
-
{:name => 'maintenance_date_locked', :column => 8},
-
{:name => 'mileage_locked', :column => 9},
-
-
{:name => 'maintenance_type', :column => 10},
-
{:name => 'maintenance_date', :column => 11},
-
{:name => 'mileage', :column => 12},
-
{:name => 'maintenance_notes', :column => 13}
-
])
-
else
-
styles.concat([
-
{:name => 'maintenance_type_locked', :column => 7},
-
{:name => 'maintenance_date_locked', :column => 8},
-
-
{:name => 'maintenance_type', :column => 9},
-
{:name => 'maintenance_date', :column => 10},
-
{:name => 'maintenance_notes', :column => 11}
-
])
-
end
-
-
styles
-
end
-
-
def row_types
-
types = [
-
# Asset Id Block
-
:string,
-
:string,
-
:string,
-
:string,
-
:string,
-
:string,
-
]
-
types << :string
-
-
if include_mileage_columns?
-
types.concat([
-
:string,
-
:date,
-
:integer,
-
:string,
-
:date,
-
:integer,
-
:string
-
])
-
else
-
types.concat([
-
:string,
-
:date,
-
:string,
-
:date,
-
:string
-
])
-
end
-
types
-
end
-
# Merge the base class styles with BPT specific styles
-
def styles
-
a = []
-
a << super
-
-
# Header Styles
-
a << {:name => 'asset_id_col', :bg_color => "EBF1DE", :fg_color => '000000', :b => false, :alignment => { :horizontal => :center, :wrap_text => true }}
-
-
a << {:name => 'maintenance_type_locked', :bg_color => "b0d6f1", :alignment => { :horizontal => :center, :wrap_text => true } , :locked => true }
-
a << {:name => 'mileage_locked', :num_fmt => 3, :bg_color => "b0d6f1", :alignment => { :horizontal => :center, :wrap_text => true } , :locked => true }
-
a << {:name => 'maintenance_date_locked', :format_code => 'MM/DD/YYYY', :bg_color => "b0d6f1", :alignment => { :horizontal => :center, :wrap_text => true } , :locked => true }
-
a << {:name => 'maintenance_type', :bg_color => "b0d6f1", :alignment => { :horizontal => :center, :wrap_text => true } , :locked => false }
-
a << {:name => 'mileage', :num_fmt => 3, :bg_color => "b0d6f1", :alignment => { :horizontal => :center, :wrap_text => true } , :locked => false }
-
a << {:name => 'maintenance_date', :format_code => 'MM/DD/YYYY', :bg_color => "b0d6f1", :alignment => { :horizontal => :center, :wrap_text => true } , :locked => false }
-
a << {:name => 'maintenance_notes', :bg_color => "b0d6f1", :alignment => { :horizontal => :left, :wrap_text => true } , :locked => false}
-
-
a.flatten
-
end
-
-
def worksheet_name
-
'Updates'
-
end
-
-
private
-
-
def initialize(*args)
-
super
-
end
-
-
def include_mileage_columns?
-
-
if @asset_class_name && (@asset_class_name.include? "Vehicle")
-
true
-
elsif @assets && (@assets.very_specific.class.to_s.include? 'Vehicle')
-
true
-
else
-
false
-
end
-
end
-
-
end
-
class NewInventoryFileHandler < AbstractFileHandler
-
-
# Perform the processing
-
def process(upload)
-
-
file_url = upload.file.url
-
-
@new_status = FileStatusType.find_by_name("Complete")
-
@num_rows_processed = 0
-
@num_rows_added = 0
-
@num_rows_replaced = 0
-
@num_rows_failed = 0
-
@processing_log = "Process complete"
-
-
end
-
-
# Init
-
def initialize(upload)
-
super
-
self.upload = upload
-
end
-
-
end
-
#------------------------------------------------------------------------------
-
# ServiceStatusUpdateEventLoader
-
#
-
# Registers a service status update event against an asset
-
#
-
# Inputs: array ['status', 'date']
-
#
-
#------------------------------------------------------------------------------
-
class ServiceStatusUpdateEventLoader < EventLoader
-
-
CURRENT_STATUS_COL = 0
-
EVENT_DATE_COL = 1
-
-
def process(asset, cells)
-
-
# Create a new ServiceStatusUpdateEvent
-
@event = asset.build_typed_event(ServiceStatusUpdateEvent)
-
-
# Current Service Status
-
@event.service_status_type = ServiceStatusType.search(cells[CURRENT_STATUS_COL])
-
if @event.service_status_type.nil?
-
@errors << "Could not find service status type '#{cells[CURRENT_STATUS_COL]}'."
-
end
-
-
# Event Date
-
@event.event_date = as_date(cells[EVENT_DATE_COL])
-
-
end
-
-
private
-
def initialize
-
super
-
end
-
-
end
-
#------------------------------------------------------------------------------
-
#
-
# SpreadsheetReader
-
#
-
# Utility class for reading a spreadsheet. Uses the roo gem to open and read the
-
# spreadsheet. Assumes that the spreadsheet is accessed via a URI so that files
-
# stored on AWS S3 can be processed in place
-
#
-
#------------------------------------------------------------------------------
-
-
class SpreadsheetReader
-
-
# Get the ith row as an array of ruby objects
-
def read(row_number)
-
-
@row = []
-
@row_number = row_number
-
if @row_number < @first_row || @row_number > @num_rows
-
raise ArgumentError, "Invalid row #{@row_number}. Value must be between = #{@first_row} and #{@num_rows}"
-
end
-
-
@first_col.upto(@sheet.last_column) do |col|
-
@row << @sheet.cell(@row_number, col)
-
end
-
-
@row
-
end
-
-
# returns true if the row is empty, false otherwise. Note, this just tests the first 10 columns
-
def empty_row?
-
0.upto(9) do |col|
-
unless @row[col].blank?
-
return false
-
end
-
end
-
return true
-
end
-
-
# Check to see if a section of the spreasheet row is empty or not
-
def empty?(start_range, stop_range)
-
(start_range..stop_range).each do |col|
-
unless @row[col].blank?
-
return false
-
end
-
end
-
return true
-
end
-
-
def includes_sheet sheet_name
-
find_sheets.include? sheet_name
-
end
-
-
def find_sheets
-
Rails.logger.debug "Opening spreadsheet #{@file_url}"
-
-
# See what type of spreadsheet we are opening, XLSX or XLS
-
file_ext = File.extname(@file_url)
-
if file_ext == ".xlsx"
-
@sheet = Roo::Excelx.new(@file_url)
-
elsif file_ext == ".xls"
-
@sheet = Roo::Excel.new(@file_url)
-
else
-
# exit with an error if the type is something else
-
raise ArgumentError, "Invalid spreadsheet type #{file_ext} for file #{@file_url}. Expected 'xls' or 'xlsx'."
-
end
-
-
@sheet.sheets
-
end
-
-
# Open the spreadsheet so the rows can be processed
-
def open(sheet_name = default_sheet_name)
-
-
Rails.logger.debug "Opening spreadsheet #{@file_url} and setting sheet name to #{sheet_name}."
-
@sheet_name = sheet_name
-
-
# See what type of spreadsheet we are opening, XLSX or XLS
-
file_ext = File.extname(@file_url)
-
if file_ext == ".xlsx"
-
@sheet = Roo::Excelx.new(@file_url)
-
elsif file_ext == ".xls"
-
@sheet = Roo::Excel.new(@file_url)
-
else
-
# exit with an error if the type is something else
-
raise ArgumentError, "Invalid spreadsheet type #{file_ext} for file #{@file_url}. Expected 'xls' or 'xlsx'."
-
end
-
@sheet.default_sheet = sheet_name
-
-
@first_row = @sheet.first_row
-
@first_col = @sheet.first_column
-
@num_rows = @sheet.last_row
-
@num_cols = @sheet.last_column
-
Rails.logger.debug "Spreadsheet opened. values from [#{@first_row}, #{@first_col}] to [#{@sheet.last_row}, #{@sheet.last_column}]"
-
-
@row = []
-
-
end
-
-
def row
-
@row
-
end
-
-
def last_row
-
@sheet.last_row unless @sheet.nil?
-
end
-
-
def num_rows
-
@num_rows
-
end
-
-
def num_cols
-
@num_cols
-
end
-
-
# Close the spreadsheet and release resources
-
def close
-
@sheet = nil
-
@num_rows = 0
-
@num_cols = 0
-
@first_col = 0
-
@last_col = 0
-
end
-
-
private
-
-
def initialize(file_url, default_sheet_name = 'Inventory')
-
@file_url = file_url
-
@default_sheet_name = default_sheet_name
-
end
-
-
end
-
#------------------------------------------------------------------------------
-
#
-
# TemplateBuilder
-
#
-
# Base class for template builders
-
#
-
#------------------------------------------------------------------------------
-
class TemplateBuilder
-
-
attr_accessor :organization
-
attr_accessor :asset_seed_class_id
-
attr_accessor :asset_class_name
-
attr_accessor :assets
-
attr_accessor :organization_list
-
attr_accessor :is_component
-
attr_accessor :fta_asset_class_id
-
-
def build
-
-
# Create a new workbook
-
p = Axlsx::Package.new
-
wb = p.workbook
-
-
# Add the worksheet
-
sheet = wb.add_worksheet(:name => worksheet_name)
-
-
# Call back to setup any implementation specific options needed
-
setup_workbook(wb)
-
-
# setup any styles and cache them for later
-
style_cache = {}
-
styles.each do |s|
-
Rails.logger.debug s.inspect
-
style = wb.styles.add_style(s)
-
Rails.logger.debug style.inspect
-
style_cache[s[:name]] = style
-
end
-
Rails.logger.debug style_cache.inspect
-
# Add the header rows
-
title = sheet.styles.add_style(:bg_color => "00A9A9A9", :sz=>12)
-
header_rows.each do |header_row|
-
sheet.add_row header_row, :style => title
-
end
-
-
# add the rows
-
add_rows(sheet)
-
-
# Add the column styles
-
column_styles.each do |col_style|
-
Rails.logger.debug col_style.inspect
-
sheet.col_style col_style[:column], style_cache[col_style[:name]], (col_style[:options] || {})
-
end
-
-
# Add the row styles
-
row_styles.each do |row_style|
-
Rails.logger.debug row_style.inspect
-
sheet.row_style row_style[:row], style_cache[row_style[:name]], (row_style[:options] || {})
-
end
-
-
# set column widths
-
sheet.column_widths *column_widths
-
-
# Perform any additional processing
-
post_process(sheet)
-
-
# Serialize the spreadsheet to the stream and return it
-
p.to_stream()
-
-
end
-
-
protected
-
-
# Configure any other implementation specific options for the workbook
-
# such as lookup table worksheets etc.
-
def setup_workbook(workbook)
-
# nothing to do by default
-
end
-
-
# Override with new styles
-
def styles
-
[
-
{:name => 'general', :num_fmt => 0},
-
{:name => 'currency', :num_fmt => 5},
-
{:name => 'percent', :num_fmt => 9},
-
{:name => 'date', :format_code => "yyyy-mm-dd"},
-
{:name => 'text_left', :alignment => { :horizontal => :left, :vertical => :center , :wrap_text => false}},
-
{:name => 'text_center', :alignment => { :horizontal => :center, :vertical => :center , :wrap_text => false}},
-
{:name => 'text_right', :alignment => { :horizontal => :right, :vertical => :center , :wrap_text => false}}
-
]
-
end
-
-
# Override this to add column styles
-
def column_styles
-
# [
-
# {:name => 'general', :column => 0},
-
# {:name => 'general', :column => 1},
-
# {:name => 'general', :column => 2}
-
#]
-
[]
-
end
-
-
# Override this to add custom column widths
-
# Only allow: nil, or numeric values
-
def column_widths
-
[]
-
end
-
-
def row_styles
-
[]
-
end
-
-
# Override this to provide the worksheet name
-
def worksheet_name
-
'default'
-
end
-
-
# Override this to get the header rows
-
def header_rows
-
[]
-
end
-
-
# Override this at rows to the sheet
-
def add_rows(sheet)
-
# Do nothing
-
end
-
-
# Override this to process post processing
-
def post_process(sheet)
-
# Do nothing
-
end
-
-
private
-
-
def initialize(args = {})
-
args.each do |k, v|
-
self.send "#{k}=", v
-
end
-
-
if @asset_seed_class_id
-
-
@search_parameter = (@asset_class_name || Rails.application.config.asset_base_class_name).constantize.asset_seed_class_name.constantize.find_by(id: @asset_seed_class_id)
-
@asset_class_name = @search_parameter.class_name unless @asset_class_name.present?
-
end
-
end
-
-
end
-
#------------------------------------------------------------------------------
-
#
-
# TemplateBuilder
-
#
-
# Base class for template builders
-
#
-
#------------------------------------------------------------------------------
-
class UpdatedTemplateBuilder
-
-
EARLIEST_DATE = SystemConfig.instance.epoch
-
-
attr_accessor :organization
-
attr_accessor :asset_types
-
attr_accessor :assets
-
attr_accessor :organization_list
-
attr_accessor :asset_class_name
-
attr_accessor :asset_seed_class_id
-
attr_accessor :is_component
-
attr_accessor :fta_asset_class_id
-
-
def build
-
-
# Create a new workbook
-
p = Axlsx::Package.new
-
wb = p.workbook
-
-
# Call back to setup any implementation specific options needed
-
setup_workbook(wb)
-
-
# Add the worksheet
-
sheet = wb.add_worksheet(:name => worksheet_name)
-
-
setup_lookup_sheet(wb)
-
-
@pick_list_cache = {}
-
@col_widths = {}
-
add_columns(sheet)
-
@frozen_cols = sheet.sheet_view.pane.x_split
-
-
# Add headers
-
category_row = []
-
header_row = []
-
idx = 0
-
@header_category_row.each do |key, fields|
-
# category_row << key
-
fields.each do |i|
-
unless i == fields[0]
-
# category_row << ''
-
end
-
-
if fields.index(i) == 0
-
category_row << i
-
else
-
# add an empty cell except for the last since the first cell holds category text
-
category_row << '' unless fields.index(i) == fields.length-1
-
header_row << i
-
-
if !@data_validations[i].nil? && !@data_validations[i].empty?
-
column_letter = convert_index_to_letter(idx)
-
if @default_values[i].present?
-
sheet.add_data_validation("#{column_letter}3", @data_validations[i].merge({:allowBlank => false}))
-
sheet.add_data_validation("#{column_letter}4:#{column_letter}1000", @data_validations[i])
-
else
-
sheet.add_data_validation("#{column_letter}3:#{column_letter}1000", @data_validations[i])
-
end
-
end
-
idx+=1
-
end
-
end
-
end
-
-
sheet.add_row category_row
-
sheet.add_row header_row
-
-
# add the rows; must be added before to get column styles
-
add_rows(sheet)
-
-
#merge header category row and add column header styles
-
start = 0
-
@header_category_row.each do |key, fields|
-
# fields contain header category text
-
fields[1..-1].each_with_index do |val, index|
-
if !@column_styles[val].nil?
-
sheet.col_style start+index, @column_styles[val]
-
style_name_parts = @style_cache.key(@column_styles[val]).split("_")
-
if style_name_parts.include?('last')
-
sheet.rows[1].cells[start+index].style = @style_cache["#{style_name_parts[0]}_#{style_name_parts[1]}_header_#{style_name_parts[2]}"]
-
else
-
sheet.rows[1].cells[start+index].style = @style_cache["#{style_name_parts[0]}_header_#{style_name_parts[1]}"]
-
end
-
end
-
end
-
sheet.merge_cells("#{convert_index_to_letter(start)}1:#{convert_index_to_letter(start+fields.length-2)}1")
-
start += fields.length-1
-
end
-
-
# Perform any additional processing
-
post_process(sheet)
-
-
# Create List of Fields and Pick Lists tab is applicable
-
create_list_of_fields(wb)
-
create_pick_lists(wb)
-
-
# set column widths
-
sheet.column_widths *column_widths
-
-
# Serialize the spreadsheet to the stream and return it
-
p.to_stream()
-
-
end
-
-
# These had been protected but I am unclear why
-
def get_lookup_cells(lookup_table_name)
-
row = @lookups[lookup_table_name][:row]
-
column = convert_index_to_letter(@lookups[lookup_table_name][:count]-1)
-
index_pick_list((row - 1), @lookups[lookup_table_name][:count]-1)
-
return "$A$#{row}:$#{column}$#{row}"
-
end
-
-
def add_column(sheet, name, name_category, col_style, data_validation={}, *other_args)
-
# add column to header row
-
if @header_category_row[name_category].blank?
-
@header_category_row[name_category] = [name_category]
-
@header_category_row[name_category] << name
-
else
-
@header_category_row[name_category] << name
-
end
-
-
# get index
-
categories = @header_category_row.keys
-
column_index = categories[0..categories.index(name_category)].sum{|c| @header_category_row[c].length}-1
-
-
# add style
-
if @style_cache[col_style[:name]].nil?
-
@style_cache[col_style[:name]] = sheet.workbook.styles.add_style(col_style)
-
end
-
@column_styles[name] = @style_cache[col_style[:name]]
-
-
# add data validation
-
@data_validations[name] = data_validation
-
-
# add column names and data to pick lists
-
@col_widths[name_category] ||= []
-
if data_validation.key?(:formula1) && data_validation[:formula1].include?("lists!")
-
@pick_list_cache[name] ||= []
-
sheet.workbook.worksheets[2].rows[@pick_list_cache[:index]].cells.each do |cell|
-
@pick_list_cache[name] << cell.value
-
end
-
@col_widths[name_category] << nil
-
elsif name.length > 18
-
@col_widths[name_category] << name.length + 2
-
else
-
@col_widths[name_category] << 20
-
end
-
-
# set any other variables
-
unless other_args.empty?
-
(0..other_args.length/2-1).each do |arg_idx|
-
instance_variable_set("@#{other_args[arg_idx*2]}", instance_variable_get("@#{other_args[arg_idx*2]}").merge({name => other_args[arg_idx*2+1]}))
-
end
-
end
-
-
end
-
-
def create_list_of_fields(workbook)
-
# Implement in subclass.
-
end
-
-
def create_pick_lists(workbook)
-
# Implement in subclass.
-
end
-
-
def index_pick_list(row_index, count)
-
# Implement in subclass
-
end
-
protected
-
-
# Configure any other implementation specific options for the workbook
-
# such as lookup table worksheets etc.
-
def setup_workbook(workbook)
-
@style_cache = {}
-
@header_category_row = {}
-
@column_styles = {}
-
@data_validations = {}
-
end
-
-
# Override this to provide lookups
-
def setup_lookup_sheet(workbook)
-
@lookups = {}
-
end
-
-
# Override this to add custom column widths
-
# Only allow: nil, or numeric values
-
def column_widths
-
[]
-
end
-
-
# Override this to provide the worksheet name
-
def worksheet_name
-
'default'
-
end
-
-
# builder overrides this following this breakdown
-
def add_columns(sheet)
-
###### MODIFY THIS SECTION ###############
-
# add columns
-
#add_column(sheet, column_index, name, col_style, data_validation={})
-
-
-
######### END MODIFY THIS SECTION ##############
-
end
-
-
def update_column(sheet, name, column_number, name_category, col_style, data_validation, *other_args)
-
# TODO update this to start on the second row
-
if @header_category_row[name_category].blank?
-
@header_category_row[name_category] = [name]
-
else
-
@header_category_row[name_category] << name
-
end
-
-
# add style
-
if @style_cache[col_style[:name]].nil?
-
@style_cache[col_style[:name]] = sheet.workbook.styles.add_style(col_style)
-
end
-
@column_styles[name] = @style_cache[col_style[:name]]
-
-
# add data validation
-
@data_validations[name] = data_validation
-
-
# set any other variables
-
unless other_args.empty?
-
(0..other_args.length/2-1).each do |arg_idx|
-
instance_variable_set("@#{other_args[arg_idx*2]}", instance_variable_get("@#{other_args[arg_idx*2]}").merge({name => other_args[arg_idx*2+1]}))
-
end
-
end
-
-
end
-
-
-
-
def add_event_column(sheet, event_class, data_validation={}, include_latest_event=false, asset_field=nil)
-
-
-
if include_latest_event and asset_field.present?
-
add_column(sheet, asset_field.humanize.capitalize, 'Event Updates', {:name => "event_string", :bg_color => 'F2DCDB', :alignment => { :horizontal => :left }, :locked => false })
-
add_column(sheet, 'Event Date', 'Event Updates', {:name => "event_date", :format_code => 'MM/DD/YYYY', :bg_color => 'F2DCDB', :alignment => { :horizontal => :left }, :locked => false })
-
-
#TODO figure out how to get asset event latest values
-
end
-
# get column label from class name
-
event_label = event_class[0..-6].gsub(/(?<=[a-z])(?=[A-Z])/, ' ')
-
-
add_column(sheet, event_label, 'Event Updates', {:name => "event_string", :bg_color => 'F2DCDB', :alignment => { :horizontal => :left }, :locked => false }, data_validation)
-
add_column(sheet, 'Reporting Date', 'Event Updates', {:name => "event_date", :format_code => 'MM/DD/YYYY', :bg_color => 'F2DCDB', :alignment => { :horizontal => :left }, :locked => false }, {
-
:type => :whole,
-
:operator => :greaterThanOrEqual,
-
:formula1 => EARLIEST_DATE.strftime("%-m/%d/%Y"),
-
:allow_blank => true,
-
:showErrorMessage => true,
-
:errorTitle => 'Wrong input',
-
:error => "Date must be after #{EARLIEST_DATE.strftime("%-m/%d/%Y")}",
-
:errorStyle => :stop,
-
:showInputMessage => true,
-
:promptTitle => 'Purchase Date',
-
:prompt => "Date must be after #{EARLIEST_DATE.strftime("%-m/%d/%Y")}"}, 'default_values', [Date.today.strftime('%m/%d/%Y')])
-
end
-
-
# Override this at rows to the sheet
-
def add_rows(sheet)
-
# Do nothing
-
end
-
-
# Override this to process post processing
-
def post_process(sheet)
-
# Do nothing
-
end
-
-
def convert_index_to_letter(num)
-
alphabet = ('A'..'Z').to_a
-
if num < 26
-
alphabet[num]
-
else
-
alphabet[num/26-1] + alphabet[num%26]
-
end
-
end
-
-
-
private
-
-
def initialize(args = {})
-
args.each do |k, v|
-
self.send "#{k}=", v
-
end
-
-
@search_parameter = (@asset_class_name || Rails.application.config.asset_base_class_name).constantize.asset_seed_class_name.constantize.find_by(id: @asset_seed_class_id)
-
@asset_class_name = @search_parameter.class_name unless @asset_class_name.present?
-
end
-
-
end
-
1
module FiscalYearHelper
-
1
def get_fy_label
-
SystemConfig.instance.default_fiscal_year_formatter ? 'Year' : 'FY'
-
end
-
-
1
def get_fiscal_year_label
-
SystemConfig.instance.default_fiscal_year_formatter ? 'Year' : 'Fiscal Year'
-
end
-
-
1
module_function :get_fy_label
-
1
module_function :get_fiscal_year_label
-
end
-
-
1
module NoticesHelper
-
# return string containing start and end times of notice
-
1
def notice_endpoints(n)
-
2
"#{n.display_datetime.strftime("%b %d, %Y %l:%M%p")} - #{n.end_datetime.strftime("%b %d, %Y %l:%M%p")}"
-
end
-
-
# return notice subject with added inactive indicator if needed
-
1
def notice_title(notice)
-
3
notice_title = notice.subject
-
3
if notice.end_datetime < DateTime.current or not notice.active
-
1
notice_title << "<small class='text-danger'> (Inactive)</small>"
-
end
-
3
return notice_title.html_safe
-
end
-
-
end
-
1
module TransamFormatHelper
-
-
# Include the fiscal year mixin
-
1
include FiscalYear
-
-
-
# Formats text as as HTML using simple_format
-
1
def format_as_text(val, sanitize=false)
-
15
simple_format(val, {}, :sanitize => sanitize)
-
end
-
-
# Formats a user name and provides message link and optional messaging options
-
# available via the options hash
-
1
def format_as_message_link(user, options = {})
-
html = ''
-
unless user.blank?
-
options[:to_user] = user
-
options[:subject] = options[:subject] || ''
-
options[:body] = options[:body] || ''
-
message_url = new_user_message_path(current_user, options)
-
html = "<a href='#{message_url}'>#{user.email}"
-
html << ' '
-
html << "<i class = 'fa fa-envelope'></i>"
-
html << "</a>"
-
end
-
html.html_safe
-
end
-
-
-
# Formats a user name and provides an optional (defaulted) message link and
-
# messaging options
-
1
def format_as_user_link(user, options = {})
-
html = ''
-
unless user.blank?
-
options[:to_user] = user
-
options[:subject] = options[:subject] || ''
-
options[:body] = options[:body] || ''
-
user_url = user_path(user)
-
html = "<a href='#{user_url}'>#{user}</a>"
-
from_user = options[:from_user]
-
if from_user.present?
-
message_url = new_user_message_path(from_user, options)
-
html << ' '
-
html << "<span class = 'message-link'>"
-
html << "<a href='#{message_url}'>"
-
html << "<i class = 'fa fa-envelope'></i>"
-
html << "</a>"
-
html << "</span>"
-
end
-
end
-
html.html_safe
-
end
-
-
# Formats a quantity as an integer followed by a unit type
-
1
def format_as_quantity(count, unit_type = 'unit')
-
1
unless unit_type.blank?
-
1
"#{format_as_integer(count)} #{unit_type}"
-
else
-
"#{count}"
-
end
-
end
-
-
# formats an assets list of asset groups with remove option
-
1
def format_asset_groups(asset, style = 'info')
-
html = ""
-
asset.asset_groups.each do |grp|
-
html << "<span class='label label-#{style}'>"
-
html << grp.code
-
html << "<span data-role='remove' data-action-path='#{remove_from_group_inventory_path(asset, :asset_group => grp)}'></span>"
-
html << "</span>"
-
end
-
html.html_safe
-
end
-
-
# formats a collection of objecsts as labels/tags. By default labels are displayed
-
# using label-info but can be controlled using the optional style param. Label text
-
# is generated using to_s unless the object has a 'code' method
-
1
def format_as_labels(coll, style = 'info')
-
html = ''
-
coll.each do |e|
-
if e.respond_to? :code
-
txt = e.code
-
else
-
txt = e.to_s
-
end
-
html << format_as_label(txt, style)
-
end
-
html.html_safe
-
end
-
-
# formats an element as a label. By default labels are displayed
-
# using label-info but can be controlled using the optional style param
-
1
def format_as_label(elem, style = 'info')
-
1
html = "<span class='label label-#{style}'>"
-
1
html << elem.to_s
-
1
html << "</span>"
-
1
html.html_safe
-
end
-
-
# formats a year value as a fiscal year string 'FY XX-YY'
-
1
def format_as_fiscal_year(val, klass = nil)
-
10
fiscal_year(val, klass) unless val.nil?
-
end
-
-
# formats a URL as a link
-
1
def format_as_url(url, target = '_blank')
-
1
link_to(url, url, :target => target)
-
end
-
-
# if no precision is set this truncates any decimals and returns the number as currency
-
1
def format_as_currency(val, precision = 0, negative_format: "%u%n")
-
14
val ||= 0
-
14
if precision == 0
-
14
if val < 0
-
val = val - 0.5
-
else
-
14
val = val + 0.5
-
end
-
14
number_to_currency(val.to_i, :precision => 0, negative_format: negative_format)
-
else
-
number_to_currency(val, :precision => precision, negative_format: negative_format)
-
end
-
end
-
-
# if the value is a number it is formatted as a decimal or integer
-
# otherwise we assume it is a string and is returned
-
1
def format_as_general(val, precision = nil)
-
begin
-
Float(val)
-
precision ||= (val % 1 == 0) ? 0 : 2
-
number_with_precision(val, :precision => precision, :delimiter => ",")
-
rescue
-
val
-
end
-
end
-
-
# truncates any decimals and returns the number with thousands delimiters
-
1
def format_as_integer(val)
-
21
format_as_decimal(val, 0)
-
end
-
-
# returns a number as a decimal
-
1
def format_as_decimal(val, precision = 2)
-
25
number_with_precision(val, :precision => precision, :delimiter => ",")
-
end
-
-
# returns a number as a percentage
-
1
def format_as_percentage(val, precision = 0)
-
3
"#{number_with_precision(val, :precision => precision)}%"
-
end
-
-
# returns a number formatted as a phone number
-
1
def format_as_phone_number(val, area_code = true)
-
12
number_to_phone(val, :area_code => area_code)
-
end
-
-
# returns a collection as a formatted list
-
1
def format_as_list(coll)
-
9
html = "<ul class='list-unstyled'>"
-
9
coll.each do |e|
-
7
html << "<li>"
-
7
html << e.to_s
-
7
html << "</li>"
-
end
-
9
html << "</ul>"
-
9
html.html_safe
-
end
-
-
# returns a collection as a formatted table without headers
-
1
def format_as_table_without_headers(data, number_of_columns = 5, cell_padding_in_px = '6px')
-
html = "<table class='table-unstyled'>"
-
counter = 0
-
-
data.each do |datum|
-
if counter == 0
-
html << '<tr>'
-
end
-
-
html << "<td style='padding:#{cell_padding_in_px};'>"
-
html << datum.to_s
-
html << "</td>"
-
-
counter += 1
-
-
if ( (counter >= number_of_columns) || (datum.equal? data.last))
-
html << '</tr>'
-
counter = 0
-
end
-
-
-
end
-
html << "</table>"
-
html.html_safe
-
end
-
-
# formats a boolean field using a checkbox if the value is true
-
1
def format_as_checkbox(val, text_class='text-default')
-
if val
-
return "<i class='fa fa-check-square-o #{text_class}'></i>".html_safe
-
else
-
return "<i class='fa fa-square-o #{text_class}'></i>".html_safe
-
end
-
end
-
-
# formats a boolean field using a flag if the value is true
-
1
def format_as_boolean(val, icon="fa-check", text_class='text-default')
-
309
if val
-
306
return "<i class='fa #{icon} #{text_class}'></i>".html_safe
-
else
-
3
return "<i class='fa #{icon} #{text_class}' style = 'visibility: hidden;'></i>".html_safe
-
end
-
end
-
-
# formats a boolean field as Yes or No
-
1
def format_as_yes_no(val)
-
1
if val
-
return "Yes"
-
else
-
1
return "No"
-
end
-
end
-
-
# Formats a date as a day date eg Mon 24 Oct
-
1
def format_as_day_date(date)
-
date.strftime("%a %d %b") unless date.nil?
-
end
-
-
# formats a date/time as a distance in words. e.g. 6 days ago
-
1
def format_as_date_time_distance(datetime)
-
4
dist = distance_of_time_in_words_to_now(datetime)
-
4
if Time.current > datetime
-
2
dist = dist + " ago"
-
end
-
4
return dist
-
end
-
-
# formats a date/time, where use_slashes indicates eg 10/24/2014 instead of 24 Oct 2014
-
1
def format_as_date_time(datetime, use_slashes=true)
-
36
if use_slashes
-
36
datetime.strftime("%m/%d/%Y %I:%M %p") unless datetime.nil?
-
else
-
datetime.strftime("%b %d %Y %I:%M %p ") unless datetime.nil?
-
end
-
end
-
-
# formats a date, where use_slashes indicates eg 10/24/2014 instead of 24 Oct 2014
-
1
def format_as_date(date, use_slashes=true, blank: '')
-
913
unless date&.year == 1
-
913
if date.nil?
-
859
blank
-
else
-
54
if use_slashes
-
54
date.strftime("%m/%d/%Y")
-
else
-
date.strftime("%b %d %Y")
-
end
-
end
-
end
-
end
-
-
# formats a time as eg " 8:00 am" or "11:00 pm"
-
1
def format_as_time(time)
-
48
return time.strftime("%l:%M %p") unless time.nil?
-
end
-
-
# formats a time as eg "08:00" or "23:00"
-
1
def format_as_military_time(time)
-
return time.strftime("%H:%M") unless time.nil?
-
end
-
-
# formats a number of seconds as the corresponding days, hours, minutes, and optional seconds
-
1
def format_as_time_difference(s, show_seconds = false)
-
return if s.blank?
-
dhms = [60,60,24].reduce([s]) { |m,o| m.unshift(m.shift.divmod(o)).flatten }
-
val = []
-
val << "#{dhms[0]} days" unless dhms[0] == 0
-
val << "#{dhms[1]}h" unless dhms[1] == 0
-
val << "#{dhms[2]}m"
-
val << "#{dhms[3]}s" if show_seconds
-
val.join(' ')
-
end
-
-
# formats an object containing US address fields as html
-
1
def format_as_address(m)
-
10
full_address = []
-
10
full_address << m.address1 unless m.address1.blank?
-
10
full_address << m.address2 unless m.address2.blank?
-
-
10
address3 = []
-
10
address3 << m.city unless m.city.blank?
-
10
address3 << m.state unless m.state.blank?
-
10
address3 << m.zip unless m.zip.blank?
-
10
address3 = address3.compact.join(', ')
-
-
10
full_address << address3
-
10
full_address = full_address.compact.join('<br/>')
-
-
10
return full_address.html_safe
-
end
-
-
# formats an unconstrained string as a valid HTML id
-
1
def format_as_id(val)
-
22
val.parameterize.underscore
-
end
-
-
# formats a label/value combination, providing optional popover support
-
1
def format_field(label, value, popover_text=nil, popover_iconcls=nil, popover_label=nil, popover_location='value')
-
-
187
html = "<div class='row control-group'>"
-
187
html << "<div class='col-xs-5 display-label'>"
-
187
html << label
-
187
if popover_location=='label' && popover_text.present?
-
popover_iconcls = 'fa fa-info-circle info-icon' unless popover_iconcls
-
popover_label = label unless popover_label
-
html << "<i class='#{popover_iconcls} info-icon' data-toggle='popover' data-trigger='hover' title='#{popover_label}' data-placement='right' data-content='#{popover_text}'></i>"
-
end
-
187
html << "</div>"
-
187
html << "<div class='col-xs-7 display-value'>"
-
187
html << value.to_s unless value.nil?
-
187
if popover_location=='value' && popover_text.present?
-
1
popover_iconcls = 'fa fa-info-circle info-icon' unless popover_iconcls
-
1
popover_label = label unless popover_label
-
1
html << "<i class='#{popover_iconcls} info-icon' data-toggle='popover' data-trigger='hover' title='#{popover_label}' data-placement='right' data-content='#{popover_text}'></i>"
-
end
-
187
html << "</div>"
-
187
html << "</div>"
-
-
187
return html.html_safe
-
-
end
-
-
# formats a value using the indicated format
-
1
def format_using_format(val, format)
-
case format
-
when :currencyM
-
number_to_currency(val, format: '%u%nM', negative_format: '(%u%nM)')
-
when :currency
-
format_as_currency(val)
-
when :fiscal_year
-
format_as_fiscal_year(val.to_i) unless val.nil?
-
when :integer
-
format_as_integer(val)
-
when :decimal
-
format_as_decimal(val)
-
when :percent
-
format_as_percentage(val)
-
when :string
-
val
-
when :checkbox
-
format_as_checkbox(val)
-
when :boolean
-
# Check for 1/0 val as well as true/false given direct query clause
-
format_as_boolean(val == 0 ? false : val)
-
when :list
-
format_as_list(val)
-
else
-
# Note, current implementation uses rescue and is thus potentially inefficient.
-
# Consider alterantives.
-
format_as_general(val)
-
end
-
end
-
-
end
-
1
module TransamHelper
-
-
# From the application config
-
1
APP_VERSION = Rails.application.config.version
-
-
1
WARNING_CLASS = "warning"
-
1
ERROR_CLASS = "error"
-
-
1
ACTIVE_CLASS = 'active'
-
1
INACTIVE_CLASS = 'inactive'
-
-
# Include the fiscal year mixin
-
1
include FiscalYear
-
-
1
def get_planning_fiscal_years_collection
-
get_planning_fiscal_years
-
end
-
-
1
def get_fiscal_years_collection
-
get_fiscal_years
-
end
-
-
1
def get_system_asset_starting_year
-
SystemConfig.instance.epoch.year
-
end
-
-
# return collection of earliest year to current year
-
1
def get_years_to_date_collection
-
(get_system_asset_starting_year..Date.today.year).to_a
-
end
-
-
# Return the version of TransAM core that is running
-
1
def transam_version
-
1
Gem.loaded_specs['transam_core'].version
-
end
-
-
# Returns the application version. This is the version defined in the host app
-
# not the version of transam
-
1
def app_version
-
1
APP_VERSION
-
end
-
-
# returns application title set in environment, with fallback
-
1
def app_title
-
4
title = ENV["APPLICATION_TITLE"] ? ENV["APPLICATION_TITLE"] : 'TransAM Application'
-
4
return title.html_safe
-
end
-
-
# returns credits set in environment, with fallback
-
1
def credits
-
credit = ENV["APPLICATION_CREDITS"] ? ENV["APPLICATION_CREDITS"] : 'TransAM Core Asset Management Platform<br/>Configure me in Application.yml'
-
return credit.html_safe
-
end
-
-
1
def html_help_path
-
3
Rails.application.config.help_directory + '/TransAMUserGuide.html'
-
end
-
-
1
def html_help_pdf_path(use_admin=false)
-
1
config = Rails.application.config
-
1
file = 'TransAMUserGuide.html'
-
1
if use_admin && config.try(:admin_guide).present?
-
file = config.admin_guide
-
1
elsif config.try(:user_guide).present?
-
file = config.user_guide
-
end
-
1
"#{config.help_directory}/#{file}"
-
end
-
-
# Returns the correct FontAwesomne icon for a file type based on the
-
# file extension
-
1
def get_file_icon_for_filename(filename)
-
ext = filename.split('.').last
-
if ["doc", "docx"].include? ext
-
icon = 'fa-file-word-o'
-
elsif ["xls", "xlsx"].include? ext
-
icon = 'fa-file-excel-o'
-
elsif ["pdf"].include? ext
-
icon = 'fa-file-pdf-o'
-
elsif ["ppt"].include? ext
-
icon = 'fa-file-powerpoint-o'
-
else
-
icon = 'fa-file-o'
-
end
-
icon
-
end
-
-
# Returns the tag icon classes (empty star or filled star) for an object based on
-
# whether the object has been tagged by the user or not
-
1
def get_tag_icon(obj)
-
9
if obj.tagged? current_user
-
'fa-star text-tagged taggable'
-
else
-
9
'fa-star-o text-tagged taggable'
-
end
-
end
-
-
# Returns the message priority icon classes for an message based on
-
# the message priority
-
1
def get_message_priority_icon(msg)
-
9
if msg.priority_type_id == 1
-
9
elsif msg.priority_type_id == 2
-
'fa-flag text-default'
-
9
elsif msg.priority_type_id == 3
-
9
'fa-flag text-danger'
-
end
-
end
-
-
# Returns the correct icon for a workflow event
-
1
def get_workflow_event_icon(event_name)
-
-
16
if event_name == 'retract'
-
'fa-eject'
-
16
elsif event_name == 'transmit' || event_name == 'submit'
-
'fa-share'
-
16
elsif event_name == 'accept' || event_name == 'authorize'
-
'fa-check-square-o'
-
16
elsif event_name == 'start' || event_name == 'publish'
-
4
'fa-play'
-
12
elsif event_name == 'complete' || event_name == 'close'
-
4
'fa-check-square'
-
8
elsif event_name == 'cancel'
-
4
'fa-stop'
-
4
elsif event_name == 'not_authorize'
-
'fa-ban'
-
4
elsif event_name == 're_start'
-
'fa-play'
-
4
elsif event_name == 'halt'
-
4
'fa-pause'
-
elsif event_name == 'retract' || event_name == 'reopen'
-
'fa-reply'
-
elsif event_name == 'return' || event_name == 'reject' || event_name == 'unapprove'
-
'fa-chevron-circle-left'
-
elsif event_name == 'approve'
-
'fa-plus-square'
-
elsif event_name == 'approve_via_transfer'
-
'fa-chevron-circle-right'
-
else
-
''
-
end
-
end
-
-
1
def bootstrap_class_for flash_type
-
case flash_type
-
when :success
-
"alert-success alert-block"
-
when :error
-
"alert-error alert-block"
-
when :alert
-
"alert-warning alert-block"
-
when :notice
-
"alert-info alert-block"
-
else
-
flash_type.to_s
-
end
-
end
-
-
# Maps priority_types to bootstrap text-* classes
-
1
def bootstrap_class_priority_type priority_type
-
1
case priority_type.id
-
when 1
-
1
"text-default"
-
when 2
-
"text-info"
-
when 3
-
"text-danger"
-
else
-
""
-
end
-
end
-
-
# Returns the system user.
-
1
def system_user
-
# By convention, the first user is always the system user.
-
User.find_by_id(1)
-
end
-
-
# returns a date as an array of elements suitable for creating a new date in javascript
-
1
def js_date(date)
-
[date.year,(date.month) - 1,date.day].compact.join(',')
-
end
-
-
# returns a year as an array of elements suitable for creating a new date in javascript
-
1
def js_year(year)
-
[year,1,1].compact.join(',')
-
end
-
-
# Returns a list of asset keys as a delimited string
-
1
def list_to_delimited_string(list, delimiter = '|')
-
str = ""
-
list.each do |e|
-
str << e
-
str << delimiter unless e == list.last
-
end
-
str
-
end
-
-
# Returns the class for the navigation link
-
1
def get_nav_link_class(controller_name, session_var=nil, param_value=nil)
-
5
if controller_name.kind_of?(Array)
-
1
controller_name.each do |name|
-
1
val = is_active_nav(name, session_var, param_value)
-
1
if val
-
return ACTIVE_CLASS
-
end
-
end
-
else
-
4
return is_active_nav(controller_name, session_var, param_value) ? ACTIVE_CLASS : INACTIVE_CLASS
-
end
-
1
return INACTIVE_CLASS
-
end
-
-
1
def is_active_nav(controller_name, session_var=nil, param_value=nil)
-
5
val = false
-
5
if params[:controller] == controller_name
-
if session_var
-
val = (session[session_var] == param_value)
-
else
-
val = true
-
end
-
end
-
5
return val
-
end
-
-
# returns the current url with any new params tacked on
-
1
def current_url(new_params)
-
url_for params: params.permit!.merge(new_params) # allow all params already passed
-
end
-
-
end
-
1
module TransamTagHelper
-
-
# returns html for a popover
-
1
def popover_tag(text, options={})
-
-
1
tag_class = (options[:class] ||= "transam-popover")
-
1
placement = (options[:placement] ||= "auto")
-
-
1
html = "<a tabindex='0' class='#{tag_class}' role='button' data-placement='#{placement}' data-container='body' data-html='true' data-toggle='popover' data-trigger='focus'"
-
1
html << " title='#{options[:title]}'" unless options[:title].blank?
-
1
html << " data-content='#{options[:content]}'>"
-
1
if options[:icon].present?
-
1
html << "<i class='fa #{options[:icon]}'>"
-
end
-
1
html << text unless text.blank?
-
1
if options[:icon].present?
-
1
html << "</i>"
-
end
-
1
html << "</a>"
-
1
html.html_safe
-
end
-
-
# returns html for a spinner panel
-
1
def loader_panel_tag(options={})
-
-
2
msg = (options[:message] ||= "Loading...")
-
2
spinner = (options[:spinner] ||= "spinner")
-
2
size = (options[:size] ||= 3)
-
2
html_class = (options[:html_class] ||= 'ajax-loader')
-
2
html_message_class = (options[:html_message_class] ||= 'ajax-loader-message')
-
2
"<div class='#{html_class} text-center'><i class='fa fa-spin fa-#{spinner} fa-#{size}x'></i><span class='#{html_message_class}'> #{msg}</span></div>".html_safe
-
end
-
-
# returns html for the old style action "cards"
-
1
def action_thumbnail_tag(options={}, &block)
-
-
# Check to see if there is any content in the block
-
content = capture(&block)
-
if content.nil?
-
content = "<p> </p>"
-
end
-
-
html = "<div class='"
-
html << options[:class] unless options[:class].blank?
-
html << "'>"
-
html << "<div class='thumbnail action-thumbnail' data-action-path='"
-
html << options[:path]
-
html << "'>"
-
-
html << "<div class='well well-small thumbnail-content' style='padding:10px;margin-bottom:0;'>"
-
-
html << "<div class='caption' style='padding:0;'>"
-
html << "<h3 style='margin-top:0;text-overflow:ellipsis;white-space:nowrap;overflow:hidden;'>"
-
html << "<i class='"
-
html << options[:icon]
-
html << " fa-2x'></i> "
-
html << options[:title]
-
html << "</h3>"
-
html << "</div>"
-
-
html << content
-
-
html << "</div>"
-
html << "</div>"
-
html << "</div>"
-
-
-
return html.html_safe
-
end
-
-
# returns html for nav tabs that include a count of items under the tab
-
1
def nav_tab_count_tag(href, title, count)
-
engine = Haml::Engine.new("
-
%li
-
%a{:href => '#{href}', :data =>{:toggle => 'tab'}}
-
%span.nowrap
-
#{title}
-
%span.badge= #{count}
-
")
-
return engine.render.html_safe
-
end
-
-
#
-
# generic method for updating fields of any model through xeditable
-
# IF YOU UPDATE THIS METHOD, update the asset field tag version as well
-
#
-
1
def editable_field_tag(model_obj, field, label=nil, model_name: nil, required: true, type: 'text', min: nil, max: nil, suffix: '', inputclass: '', url: '')
-
asset = model_obj.is_a?(Array) ? model_obj.last : model_obj
-
-
if type == 'boolean'
-
return editable_association_tag(asset, field, label,
-
[[1, 'Yes'],[0, 'No']],
-
include_blank: !required, current_value: asset.send(field) ? 1 : 0, suffix: suffix, inputclass: '')
-
end
-
extras = ''
-
extras += ", min: #{min}" if min
-
extras += ", max: #{max}" if max
-
classes = ' '
-
classes += 'required ' if required
-
# classes += 'datepicker ' if type == 'date'
-
value = escape_javascript(asset.send(field).to_s)
-
case type
-
when 'date'
-
type = 'combodate'
-
classes += 'combodate'
-
value = format_as_date(Date.parse(value)) unless value.blank?
-
# extras += ", format: 'MM/DD/YYYY', viewformat: 'MM/DD/YYYY'"
-
when 'currency'
-
type = 'number'
-
classes += 'currency-number'
-
when 'textarea'
-
value = asset.send(field).to_s.gsub(/\r\n|\r|\n/, "<br />")
-
end
-
-
# Escape for HAML
-
label = label.gsub('%','\%') if label
-
-
if url
-
asset_path = url
-
elsif model_name.nil? || model_obj.class.to_s == model_name
-
asset_path = polymorphic_path(model_obj)
-
else
-
asset_path = polymorphic_path(model_obj.send(model_name.underscore))
-
end
-
-
engine = Haml::Engine.new("
-
##{field}_group.form-group
-
%label.control-label{class: '#{classes}'}
-
#{label || field.to_s.titleize}
-
.display-value
-
%a.editable-field{href:'#', id: '#{field}', class: '#{classes}', data: {inputclass: '#{inputclass}', emptytext: ' - ', name: '#{(model_name || asset.class.base_class.name).underscore}[#{field}]', type: '#{type}', placeholder: '#{required ? 'Required' : ''}', url: '#{asset_path}'#{extras}}}
-
\\#{value}
-
#{suffix}
-
")
-
return engine.render.html_safe
-
end
-
-
#
-
# generic method for updating associations of any model through xeditable
-
# IF YOU UPDATE THIS METHOD, update the asset association tag version as well
-
#
-
1
def editable_association_tag(model_obj, field, label=nil, collection=nil, model_name: nil, current_method: nil, include_blank: false, current_value: nil, type: 'select', url: nil, suffix: '_id')
-
-
asset = model_obj.is_a?(Array) ? model_obj.last : model_obj
-
-
# value = current_value || (collection || url ? asset.send(current_method || field).to_s : asset.send(current_method || "#{field.to_s}_id").to_s)
-
field_name = current_method || "#{field.to_s.singularize}#{suffix}"
-
value = current_value || asset.send(current_method || field_name).to_s
-
unless collection || url
-
klass = asset.association(field).reflection.class_name.constantize
-
collection = klass.active.collect{|a| [a.id, a.to_s]}
-
-
end
-
unless url
-
# The source will wind up being parsed twice by X-editable, so embedded apostrophes
-
# have to be doubly escaped.
-
source = include_blank ? "{value: '', text: ''}," : ''
-
source += collection.map{|pair| "{value: '#{pair[0]}', text: '#{pair[1].to_s.gsub("'"){"\\\\'"}}'}"}.join(',')
-
end
-
-
if model_name.nil? || model_obj.class.to_s == model_name
-
asset_path = polymorphic_path(model_obj)
-
else
-
asset_path = polymorphic_path(model_obj.send(model_name.underscore))
-
end
-
-
engine = Haml::Engine.new("
-
##{field}_group.form-group
-
%label.control-label
-
#{label || field.to_s.titleize}
-
.display-value
-
%a.editable-field{href:'#', id: '#{field}', data: {emptytext: ' - ', name: '#{(model_name || asset.class.base_class.name).underscore}[#{field_name}]', value: '#{value}', type: '#{type}', url: '#{asset_path}', source: \"#{url || "[#{source}]"}\", sourceCache: '#{url.nil?}'}}
-
")
-
return engine.render.html_safe
-
end
-
-
#
-
# updating asset fields through xeditable
-
# IF YOU UPDATE THIS METHOD, update the generic field tag version as well
-
#
-
1
def editable_asset_field_tag(asset, field, label=nil, required: true, type: 'text', min: nil, max: nil, suffix: '', inputclass: '')
-
if type == 'boolean'
-
return editable_asset_association_tag(asset, field, label,
-
[[1, 'Yes'],[0, 'No']],
-
include_blank: !required, current_value: @asset.send(field) ? 1 : 0, suffix: suffix, inputclass: inputclass)
-
end
-
extras = ''
-
extras += ", min: #{min}" if min
-
extras += ", max: #{max}" if max
-
classes = ' '
-
classes += 'required ' if required
-
# classes += 'datepicker ' if type == 'date'
-
value = escape_javascript(asset.send(field).to_s)
-
case type
-
when 'date'
-
type = 'combodate'
-
classes += 'combodate'
-
value = format_as_date(Date.parse(value)) unless value.blank?
-
# extras += ", format: 'MM/DD/YYYY', viewformat: 'MM/DD/YYYY'"
-
when 'currency'
-
type = 'number'
-
classes += 'currency-number'
-
when 'textarea'
-
value = asset.send(field).to_s.gsub(/\r\n|\r|\n/, "<br />")
-
end
-
-
# Escape for HAML
-
label = label.gsub('%','\%') if label
-
engine = Haml::Engine.new("
-
##{field}_group.form-group
-
%label.control-label{class: '#{classes}'}
-
#{label || field.to_s.titleize}
-
.display-value
-
%a.editable-field{href:'#', id: '#{field}', class: '#{classes}', data: {inputclass: '#{inputclass}', emptytext: ' - ', name: 'asset[#{field}]', type: '#{type}', placeholder: '#{required ? 'Required' : ''}', url: '#{asset_path(asset)}'#{extras}}}
-
\\#{value}
-
#{suffix}
-
")
-
return engine.render.html_safe
-
end
-
-
#
-
# updating asset associations through xeditable
-
# IF YOU UPDATE THIS METHOD, update the generic association tag version as well
-
#
-
1
def editable_asset_association_tag(asset, field, label=nil, collection=nil, current_method: nil, include_blank: false, current_value: nil, type: 'select', url: nil, suffix: '_id', inputclass: '', include_other: false)
-
# value = current_value || (collection || url ? asset.send(current_method || field).to_s : asset.send(current_method || "#{field.to_s}_id").to_s)
-
field_name = current_method || "#{field.to_s.singularize}#{suffix}"
-
value = current_value || asset.send(current_method || field_name).to_s
-
unless collection || url
-
klass = asset.association(field).reflection.class_name.constantize
-
collection = klass.active.collect{|a| [a.id, a.to_s]}
-
-
end
-
-
# Tags with url data sources will sometimes display as blank due to an ajax race condition.
-
# Here, we force in display text containing the field's to_string implementation when the data source is from a url.
-
display_text = ""
-
if url
-
display_text = (suffix == '_ids' ? asset.send(field.to_s.pluralize).map{|x| x.to_s}.join("<br>").html_safe : asset.send(field.to_s).to_s)
-
else
-
# The source will wind up being parsed twice by X-editable, so embedded apostrophes
-
# have to be doubly escaped.
-
source = include_blank ? "{value: '', text: ''}," : ''
-
source += collection.map{|pair| "{value: '#{pair[0]}', text: '#{pair[1].to_s.gsub("'"){"\\\\'"}}'}"}.join(',')
-
if include_other
-
source += "," unless source[-1] == ","
-
source += "{value: '#{TransamAsset::DEFAULT_OTHER_ID}', text: 'Other'}" if include_other
-
end
-
end
-
engine = Haml::Engine.new("
-
##{field}_group.form-group
-
%label.control-label
-
#{label || field.to_s.titleize}
-
.display-value
-
%a.editable-field{href:'#', id: '#{field}', data: {inputclass: '#{inputclass}', emptytext: ' - ', name: 'asset[#{field_name}]', value: '#{value}', type: '#{type}', url: '#{asset_path(asset)}', source: \"#{url || "[#{source}]"}\", sourceCache: '#{url.nil?}'}} #{display_text}
-
")
-
return engine.render.html_safe
-
end
-
-
# returns html for a panel comprising a subcomponent of a form
-
1
def dialog_tag(dialog_name, options={}, &block)
-
# Check to see if there is any content in the block
-
18
content = capture(&block)
-
18
if content.nil?
-
content = "<p> </p>"
-
end
-
-
18
id = options[:id]
-
18
id_str = id ? "id='#{id}'" : "id='#{format_as_id(dialog_name)}_dialog'"
-
18
html = "<div class='panel panel-default #{options[:class]}' #{id_str}>"
-
18
html << "<div class='panel-heading'>"
-
18
html << "<h3 class='panel-title'>"
-
18
unless options[:icon].blank?
-
18
html << "<i class='"
-
18
html << options[:icon]
-
18
html << "'></i> "
-
end
-
18
html << dialog_name
-
18
html << "</h3>"
-
18
unless options[:link_path].blank?
-
html << "<div style='float:right;position:relative;top:-15px;'>"
-
html << link_to(options[:link_text], options[:link_path], :class => "")
-
html << "</div>"
-
end
-
18
html << "</div>"
-
18
html << "<div class='panel-body'>"
-
18
html << content
-
18
html << "</div>"
-
18
html << "</div>"
-
18
return html.html_safe
-
end
-
-
end
-
class FiscalYearInput < SimpleForm::Inputs::Base
-
include FiscalYearHelper
-
-
def input
-
field_name = "#{@builder.object_name}_#{attribute_name}"
-
-
out = ActiveSupport::SafeBuffer.new
-
out << @builder.hidden_field(attribute_name).html_safe
-
out << "<div class='input-group' style='max-width:300px;'>".html_safe
-
out << "<span class='input-group-addon'>#{get_fy_label} </span>".html_safe
-
out << template.number_field_tag(:"#{attribute_name}_input", object.send(attribute_name),input_html_options.merge({for: field_name, min: 1900, placeholder: 'YYYY'})).html_safe
-
out << "<span class='input-group-addon' for='#{field_name}'> - YYYY</span>".html_safe
-
out << "</div>".html_safe
-
end
-
-
def label_target
-
:"#{attribute_name}_input_start"
-
end
-
-
-
def input_html_classes
-
super.push('form-control')
-
end
-
-
end
-
#------------------------------------------------------------------------------
-
#
-
# AbstractAssetUpdateJob
-
#
-
# Base class for jobs which perform updates on assets. All update jobs for assets
-
# should be derived from this class.
-
#
-
#------------------------------------------------------------------------------
-
1
class AbstractAssetUpdateJob < Job
-
-
1
attr_accessor :object_key
-
-
# Return true if the specific update requires that the SOGR characteristics
-
# of the asset need to also be updated.
-
1
def requires_sogr_update?
-
false
-
end
-
-
# execution phase. all concrete classes should override this
-
# method
-
#
-
1
def execute_job(a = nil)
-
-
end
-
#
-
# Define the base class run method. This simply loads the asset
-
# checks it was there and then runs the execution logic
-
#
-
1
def run
-
10
asset = Rails.application.config.asset_base_class_name.constantize.find_by_object_key(object_key)
-
10
if asset
-
# Make sure the asset is typed
-
9
a = Rails.application.config.asset_base_class_name.constantize.get_typed_asset(asset)
-
-
9
execute_job(a)
-
-
else
-
1
raise RuntimeError, "Can't find Asset with object_key #{object_key}"
-
end
-
end
-
-
1
def check
-
2
raise ArgumentError, "object_key can't be blank " if object_key.blank?
-
end
-
-
1
def initialize(object_key)
-
26
super
-
26
self.object_key = object_key
-
end
-
-
end
-
#------------------------------------------------------------------------------
-
#
-
# ActivityJob
-
#
-
# Special type of job that is run from the acticity scheduler. These jobs
-
# reference the activity context and can load parameters from the activity
-
# table
-
#
-
#------------------------------------------------------------------------------
-
1
class ActivityJob < Job
-
-
1
attr_reader :context # the activity
-
1
attr_reader :start_time
-
-
1
def initialize(args = {})
-
11
super(args)
-
11
args.each do |k,v|
-
10
instance_variable_set("@#{k}", v) unless v.nil?
-
end
-
end
-
-
# Make sure that the due date/time expression can be parsed
-
1
def check
-
3
super
-
# Save the start time for the job
-
3
@start_time = Time.now
-
3
raise ArgumentError, "Missing execution context" if @context.blank?
-
2
true
-
end
-
-
# Perform post-processing
-
1
def clean_up
-
1
super
-
# Update the last run time and save
-
1
@context.last_run = @start_time
-
1
@context.save(:validate => false)
-
end
-
-
1
def prepare
-
super
-
end
-
-
1
protected
-
-
# Write to activity log
-
1
def write_to_activity_log org, message
-
log = ActivityLog.new({
-
:item_type => self.class.name,
-
:organization => org,
-
:activity => message,
-
:activity_time => Time.now,
-
:user => system_user
-
})
-
log.save
-
end
-
-
# Get the system user
-
1
def system_user
-
User.find_by('first_name = ? AND last_name = ?', 'system', 'user')
-
end
-
-
end
-
#------------------------------------------------------------------------------
-
#
-
# AssetConditionUpdateJob
-
#
-
# Updates an assets condition
-
#
-
#------------------------------------------------------------------------------
-
1
class AssetConditionUpdateJob < AbstractAssetUpdateJob
-
-
# Force an update of the SOGR characteristics based on the new mileage
-
1
def requires_sogr_update?
-
1
true
-
end
-
-
1
def execute_job(asset)
-
1
asset.update_condition
-
end
-
-
1
def prepare
-
1
Rails.logger.debug "Executing AssetConditionUpdateJob at #{Time.now.to_s} for Asset #{object_key}"
-
end
-
-
end
-
#------------------------------------------------------------------------------
-
#
-
# AssetDependentSpatialReferenceUpdateJob
-
#
-
# Updates the derived spatial reference of all assets associated with the input asset
-
#
-
#------------------------------------------------------------------------------
-
class AssetDependentSpatialReferenceUpdateJob < AbstractAssetUpdateJob
-
-
def execute_job(asset)
-
-
# Note that if the asset geometry is null, the dependent assets also
-
# get their geometry set to null
-
geom = asset.geometry
-
count = 0
-
assets = Rails.application.config.asset_base_class_name.constantize.where(parent_id: asset.id)
-
assets.each do |a|
-
a.geometry = geom
-
a.save
-
count += 1
-
end
-
Rails.logger.info "Updated #{count} asset parents."
-
end
-
-
def prepare
-
Rails.logger.debug "Executing AssetDependentSpatialReferenceUpdateJob at #{Time.now.to_s} for Asset #{object_key}"
-
end
-
-
end
-
#------------------------------------------------------------------------------
-
#
-
# AssetDispositionUpdateJob
-
#
-
# Records that an asset has been disposed.
-
#
-
#------------------------------------------------------------------------------
-
1
class AssetDispositionUpdateJob < AbstractAssetUpdateJob
-
-
-
1
def execute_job(asset)
-
disposition_event = asset.disposition_updates.last
-
just_disposed_and_transferred = !asset.disposed? && disposition_event.try(:disposition_type_id) == 2
-
-
asset.update_columns(disposition_date: disposition_event.try(:event_date))
-
-
if(just_disposed_and_transferred)
-
new_asset = asset.transfer disposition_event.organization_id
-
send_asset_transferred_message new_asset
-
end
-
end
-
-
1
def prepare
-
1
Rails.logger.debug "Executing AssetDispositionUpdateJob at #{Time.now.to_s} for Asset #{object_key}"
-
end
-
-
1
def send_asset_transferred_message asset
-
-
transit_managers = get_users_for_organization asset.organization
-
-
event_url = Rails.application.routes.url_helpers.new_inventory_path asset
-
-
transfer_notification = Notification.create(text: "A new asset has been transferred to you. Please update the asset.", link: event_url, notifiable_type: 'Organization', notifiable_id: asset.organization_id )
-
-
transit_managers.each do |usr|
-
UserNotification.create(notification: transfer_notification, user: usr)
-
end
-
-
end
-
-
# TODO there is probably a better way
-
1
def get_users_for_organization organization
-
user_role = Role.find_by(:name => 'transit_manager')
-
-
unless user_role.nil?
-
users = organization.users_with_role user_role.name
-
end
-
-
return users || []
-
end
-
-
end
-
#------------------------------------------------------------------------------
-
#
-
# AssetLocationUpdateJob
-
#
-
# Updates an assets location
-
#
-
#------------------------------------------------------------------------------
-
1
class AssetLocationUpdateJob < AbstractAssetUpdateJob
-
-
1
def execute_job(asset)
-
asset.update_location
-
end
-
-
1
def prepare
-
Rails.logger.debug "Executing AssetLocationUpdateJob at #{Time.now.to_s} for Asset #{object_key}"
-
end
-
-
end
-
#------------------------------------------------------------------------------
-
#
-
# AssetMaintenanceUpdateJob
-
#
-
# Records an assets maintenance event
-
#
-
#------------------------------------------------------------------------------
-
1
class AssetMaintenanceUpdateJob < AbstractAssetUpdateJob
-
-
# Force an update of the SOGR characteristics based on the new mileage
-
1
def requires_sogr_update?
-
1
false
-
end
-
-
1
def execute_job(asset)
-
asset.update_maintenance
-
end
-
-
1
def prepare
-
1
Rails.logger.debug "Executing AssetMaintenanceUpdateJob at #{Time.now.to_s} for Asset #{object_key}"
-
end
-
-
end
-
#------------------------------------------------------------------------------
-
#
-
# AssetRehabilitationUpdateJob
-
#
-
# Updates an assets rehabilitation ststus
-
#
-
#------------------------------------------------------------------------------
-
1
class AssetRehabilitationUpdateJob < AbstractAssetUpdateJob
-
-
1
def requires_sogr_update?
-
false # manually call it in job so can perform update rehab actions in correct order
-
end
-
-
1
def execute_job(asset)
-
1
asset.update_rehabilitation
-
-
1
asset.update_sogr
-
-
1
update_sched_replacement_yr = false
-
-
1
if asset.rehabilitation_updates.empty?
-
update_sched_replacement_yr = true
-
else
-
1
last_rehab = asset.rehabilitation_updates.last
-
1
if last_rehab.extended_useful_life_months > 0 || (last_rehab.try(:extended_useful_life_miles) || 0) > 0
-
update_sched_replacement_yr = true
-
end
-
end
-
-
1
if update_sched_replacement_yr
-
asset.update(scheduled_replacement_year: asset.policy_replacement_year)
-
end
-
-
end
-
-
1
def prepare
-
1
Rails.logger.debug "Executing AssetRehabilitationUpdateJob at #{Time.now.to_s} for Asset #{object_key}"
-
end
-
-
end
-
#------------------------------------------------------------------------------
-
#
-
# AssetScheduleDispositionUpdateJob
-
#
-
# Updates an assets scheduled disposition
-
#
-
#------------------------------------------------------------------------------
-
1
class AssetScheduleDispositionUpdateJob < AbstractAssetUpdateJob
-
-
1
def execute_job(asset)
-
1
asset.update_scheduled_disposition
-
end
-
-
1
def prepare
-
1
Rails.logger.debug "Executing AssetScheduleDispositionUpdateJob at #{Time.now.to_s} for Asset #{object_key}"
-
end
-
-
end
-
#------------------------------------------------------------------------------
-
#
-
# AssetScheduleRehabilitationUpdateJob
-
#
-
# Updates an assets scheduled rehabilitation year
-
#
-
#------------------------------------------------------------------------------
-
1
class AssetScheduleRehabilitationUpdateJob < AbstractAssetUpdateJob
-
-
# Force an update of the SOGR characteristics based on the new replacement date
-
1
def requires_sogr_update?
-
1
true
-
end
-
-
1
def execute_job(asset)
-
1
asset.update_scheduled_rehabilitation
-
end
-
-
1
def prepare
-
1
Rails.logger.debug "Executing AssetScheduleRehabilitationUpdateJob at #{Time.now.to_s} for Asset #{object_key}"
-
end
-
-
end
-
#------------------------------------------------------------------------------
-
#
-
# AssetScheduleReplacementUpdateJob
-
#
-
# Updates an assets scheduled replacement year
-
#
-
#------------------------------------------------------------------------------
-
1
class AssetScheduleReplacementUpdateJob < AbstractAssetUpdateJob
-
-
# Force an update of the SOGR characteristics based on the new replacement date
-
1
def requires_sogr_update?
-
1
true
-
end
-
-
1
def execute_job(asset)
-
1
asset.update_scheduled_replacement
-
end
-
-
1
def prepare
-
1
Rails.logger.debug "Executing AssetScheduleReplacementUpdateJob at #{Time.now.to_s} for Asset #{object_key}"
-
end
-
-
end
-
#------------------------------------------------------------------------------
-
#
-
# AssetDepreciationExpenseUpdateJob
-
#
-
#
-
#------------------------------------------------------------------------------
-
class AssetServiceLifeUpdateJob < ActivityJob
-
-
-
def run
-
# Service life calculators with 'AND mileage/condition' could possibly set policy replacement year = planning year + 1
-
# as planning year changes at rollover a background job has to run to update these values
-
# set this up as an activity to run once a month but activity can be changed to run as often as you want to see if the 'AND' condition has been met changing the policy replacement year
-
rules = PolicyAssetTypeRule.joins(:service_life_calculation_type).where('service_life_calculation_types.class_name LIKE ?', "%#{'And'}%").where.not(policy_id: Policy.find_by(parent_id: nil).id)
-
rules.each do |rule|
-
assets = Rails.application.config.asset_base_class_name.constantize.operational.joins(asset_subtype: :asset_type).where(organization_id: rule.policy.organization_id, asset_types: {id: rule.asset_type_id})
-
assets.each do |asset|
-
asset.save! # trigger callbacks
-
end
-
end
-
-
end
-
-
def clean_up
-
super
-
Rails.logger.debug "Completed AssetServiceLifeUpdateJob at #{Time.now.to_s}"
-
end
-
-
def prepare
-
super
-
Rails.logger.debug "Executing AssetServiceLifeUpdateJob at #{Time.now.to_s}"
-
end
-
-
end
-
#------------------------------------------------------------------------------
-
#
-
# AssetServiceStatusUpdateJob
-
#
-
# Updates an assets service status
-
#
-
#------------------------------------------------------------------------------
-
1
class AssetServiceStatusUpdateJob < AbstractAssetUpdateJob
-
-
1
def execute_job(asset)
-
1
asset.update_service_status
-
end
-
-
1
def prepare
-
1
Rails.logger.debug "Executing AssetServiceStatusUpdateJob at #{Time.now.to_s} for Asset #{object_key}"
-
end
-
-
end
-
#------------------------------------------------------------------------------
-
#
-
# AssetSogrUpdateJob
-
#
-
# Updates an assets SOGR state
-
#
-
#------------------------------------------------------------------------------
-
1
class AssetSogrUpdateJob < AbstractAssetUpdateJob
-
-
1
def execute_job(asset)
-
1
asset.update_sogr
-
end
-
-
1
def prepare
-
1
Rails.logger.debug "Executing AssetSogrUpdateJob at #{Time.now.to_s} for Asset #{object_key}"
-
end
-
-
end
-
#------------------------------------------------------------------------------
-
#
-
# AssetUpdateJob
-
#
-
# Performs all updates on an asset
-
#
-
#------------------------------------------------------------------------------
-
class AssetUpdateAllJob < Job
-
-
attr_accessor :organization
-
attr_accessor :asset_types
-
attr_accessor :creator
-
-
def run
-
-
# Rip through the organizations assets, creating a job for each type requested
-
org = Organization.get_typed_organization(organization)
-
assets = org.assets.operational.where(asset_type: asset_types)
-
count = assets.count
-
assets.find_each do |a|
-
typed_asset = Asset.get_typed_asset(a)
-
typed_asset.update_methods.each do |m|
-
begin
-
typed_asset.send(m, false) #dont save until all updates have run
-
rescue Exception => e
-
Rails.logger.warn e.message
-
end
-
end
-
typed_asset.save
-
end
-
-
policy = Policy.active.find_by(organization_id: organization.id)
-
if count > 0
-
msg = "#{count} assets have been updated using policy #{policy}."
-
# Add a row into the activity table
-
ActivityLog.create({:organization_id => organization.id, :user_id => creator.id, :item_type => "Policy Asset Update", :activity => msg, :activity_time => Time.current})
-
else
-
msg = "No assets were updated."
-
end
-
-
event_url = Rails.application.routes.url_helpers.policy_path policy
-
policy_notification = Notification.create(text: msg, link: event_url, notifiable_type: 'Organization', notifiable_id: policy.organization_id)
-
UserNotification.create(user: creator, notification: policy_notification)
-
-
end
-
-
def prepare
-
Rails.logger.debug "Executing AssetUpdateAllJob at #{Time.now.to_s} for all assets"
-
end
-
-
def check
-
raise ArgumentError, "organization can't be blank " if organization.nil?
-
raise ArgumentError, "asset_types can't be blank " if asset_types.nil?
-
raise ArgumentError, "creator can't be blank " if creator.nil?
-
end
-
-
def initialize(organization, asset_types, creator)
-
super
-
self.organization = organization
-
self.asset_types = asset_types
-
self.creator = creator
-
end
-
-
end
-
#------------------------------------------------------------------------------
-
#
-
# AssetUpdateJob
-
#
-
# Performs all updates on an asset
-
#
-
#------------------------------------------------------------------------------
-
1
class AssetUpdateJob < AbstractAssetUpdateJob
-
-
1
def requires_sogr_update?
-
1
true
-
end
-
-
# If a generic Asset is passed, we run an incomplete list of update methods
-
1
def execute_job(typed_asset)
-
2
update_methods = typed_asset.update_methods
-
-
# Is SOGR status expensive to update?
-
#unless requires_sogr_update?
-
# update_methods.remove(:update_sogr)
-
#end
-
-
2
update_methods.each do |method|
-
16
typed_asset.send(method, false) #dont save until all updates have run
-
end
-
1
typed_asset.save
-
end
-
-
1
def prepare
-
2
Rails.logger.debug "Executing AssetUpdateJob at #{Time.now.to_s} for Asset #{object_key}"
-
end
-
-
end
-
#-------------------------------------------------------------------------------
-
# AuditRunnerJob
-
#
-
# Activity Job runner that runs an audit. The activity is set as the run time
-
# context. This is used to get the corresponding audit from the audit table
-
#-------------------------------------------------------------------------------
-
class IssuesReportJob < ActivityJob
-
-
include TransamFormatHelper
-
-
def run
-
-
message_template = MessageTemplate.find_by(name: 'Support1')
-
if message_template.present?
-
system_user = User.where(first_name: 'system', last_name: 'user').first
-
-
event_url = Rails.application.routes.url_helpers.report_url(Report.find_by(name: 'Issues Report'))
-
-
# send message to all admin about new issues created this week
-
activity = Activity.find_by(job_name: self.class.name)
-
frequency = activity.frequency_type.name
-
new_issues = Issue.where(issue_status_type: IssueStatusType.find_by(name: 'Open')).where('created_at >= ?', Date.today - (activity.frequency_quantity).send(frequency))
-
-
message_body = MessageTemplateMessageGenerator.new.generate(message_template, [new_issues.count, frequency, "<a href='#{event_url}'>here</a>"])
-
-
if new_issues.count > 0
-
User.with_role(:admin).each do |user|
-
msg = Message.new
-
msg.user = system_user
-
msg.organization = system_user.organization
-
msg.to_user = user
-
msg.subject = message_template.subject
-
msg.body = message_body
-
msg.priority_type = message_template.priority_type
-
msg.message_template = message_template
-
msg.active = message_template.active
-
msg.save
-
end
-
end
-
-
write_to_activity_log system_user.organization, "Issues Report sent to admin users"
-
end
-
-
end
-
-
def clean_up
-
super
-
Rails.logger.debug "Completed IssuesReportJob at #{Time.now.to_s}"
-
end
-
-
def prepare
-
super
-
Rails.logger.debug "Executing IssuesReportJob at #{Time.now.to_s}"
-
end
-
end
-
#------------------------------------------------------------------------------
-
#
-
# Job
-
#
-
# Base class for all jobs. This class represents a generic job that can be run
-
# by the background processor. All background jobs should be derived from this
-
# class
-
#
-
#------------------------------------------------------------------------------
-
1
class Job
-
-
1
def initialize(*args)
-
end
-
-
# Called by the delayed_jobs scheduler to execute a job
-
1083
def perform
-
begin
-
# prepare the job
-
1082
prepare
-
# check that everything is kosher
-
1081
check
-
# run the job
-
1081
run
-
# perform any post processing`
-
1035
clean_up
-
47
rescue Exception => e
-
47
PutMetricDataService.new.put_metric('DelayedJobErrorResponse', 'Count', 1)
-
-
47
Rails.logger.warn e.message
-
47
Rails.logger.warn e.backtrace
-
47
Delayed::Worker.logger.warn e.message rescue nil # log to worker logger if available
-
47
Delayed::Worker.logger.warn e.backtrace rescue nil # log to worker logger if available
-
end
-
end
-
-
1
def prepare
-
end
-
1
def check
-
end
-
1
def clean_up
-
#nothing to do
-
end
-
-
1
protected
-
-
# Get the system user
-
1
def get_system_user
-
6
User.where('first_name = ? AND last_name = ?', 'system', 'user').first
-
end
-
-
end
-
#------------------------------------------------------------------------------
-
#
-
# KeywordIndexDeleteJob
-
#
-
# Deletes the keyword index for an object
-
#
-
#------------------------------------------------------------------------------
-
1
class KeywordIndexDeleteJob < Job
-
-
1
attr_accessor :object_key
-
1
attr_accessor :class_name
-
-
1
def run
-
213
klass = class_name.constantize
-
213
if klass
-
# If we got the class then call the update method
-
213
klass.remove_from_index object_key
-
else
-
raise RuntimeError, "Can't instantiate class #{class_name}"
-
end
-
end
-
-
1
def prepare
-
212
Rails.logger.info "Executing KeywordIndexDeleteJob at #{Time.now.to_s} for Keyword Index #{object_key}"
-
end
-
-
1
def check
-
213
raise ArgumentError, "class_name can't be blank " if class_name.blank?
-
212
raise ArgumentError, "object_key can't be blank " if object_key.blank?
-
end
-
-
1
def initialize(class_name, object_key)
-
216
super
-
216
self.class_name = class_name
-
216
self.object_key = object_key
-
end
-
-
end
-
#------------------------------------------------------------------------------
-
#
-
# KeywordIndexUpdateJob
-
#
-
# Updates the keyword index for an object
-
#
-
#------------------------------------------------------------------------------
-
1
class KeywordIndexUpdateJob < Job
-
-
1
attr_accessor :object_key
-
1
attr_accessor :class_name
-
-
1
def run
-
825
begin
-
825
klass = class_name.constantize
-
rescue
-
1
raise RuntimeError, "Can't instantiate class #{class_name}"
-
end
-
824
if klass
-
824
klass_instance = klass.find_by(:object_key => object_key)
-
824
if klass_instance
-
# If we got the class then call the update method
-
822
klass_instance.write_to_index
-
else
-
2
Rails.logger.info "Can't find #{class_name} with object_key #{object_key}"
-
end
-
end
-
-
end
-
-
1
def prepare
-
823
Rails.logger.info "Executing KeywordIndexUpdateJob at #{Time.now.to_s} for Keyword Index #{object_key}"
-
end
-
-
1
def check
-
824
raise ArgumentError, "class_name can't be blank " if class_name.blank?
-
823
raise ArgumentError, "object_key can't be blank " if object_key.blank?
-
end
-
-
1
def initialize(class_name, object_key)
-
828
super
-
828
self.class_name = class_name
-
828
self.object_key = object_key
-
end
-
-
end
-
#------------------------------------------------------------------------------
-
#
-
# LockedAccountInformerJob
-
#
-
# Searches for acccounts that have been locked and sends an email to the
-
# administrators
-
#
-
#------------------------------------------------------------------------------
-
1
class LockedAccountInformerJob < Job
-
-
1
attr_accessor :object_key
-
-
1
def run
-
-
2
locked_user = User.find_by_object_key(object_key)
-
2
if locked_user
-
# Get the system user
-
1
sys_user = get_system_user
-
-
# Get the admin users
-
1
admins = User.with_role :admin
-
-
1
message_template = MessageTemplate.find_by(name: 'User3')
-
-
1
if message_template
-
1
message_body = MessageTemplateMessageGenerator.new.generate(message_template, [locked_user.name, locked_user.locked_at])
-
# Send a message to the admins for this user organization
-
1
admins.each do |admin|
-
1
msg = Message.new
-
1
msg.organization = admin.organization
-
1
msg.user = sys_user
-
1
msg.to_user = admin
-
1
msg.subject = message_template.subject
-
1
msg.body = message_body
-
1
msg.priority_type = message_template.priority_type
-
1
msg.message_template = message_template
-
1
msg.active = message_template.active
-
1
msg.save
-
end
-
end
-
-
else
-
1
raise RuntimeError, "Can't find User with object_key #{object_key}"
-
end
-
-
end
-
-
1
def prepare
-
2
Rails.logger.info "Executing LockedAccountInformerJob at #{Time.now.to_s} for User #{object_key}"
-
end
-
-
1
def check
-
1
raise ArgumentError, "object_key can't be blank " if object_key.blank?
-
end
-
-
1
def initialize(object_key)
-
5
super
-
5
self.object_key = object_key
-
end
-
-
end
-
#------------------------------------------------------------------------------
-
#
-
# LockedAccountInformerJob
-
#
-
# Searches for acccounts that have been locked and sends an email to the
-
# administrators
-
#
-
#------------------------------------------------------------------------------
-
class MessageTemplateInformerJob < Job
-
-
attr_accessor :object_key
-
-
def run
-
-
templated_changed = MessageTemplate.find_by_object_key(object_key)
-
if templated_changed
-
# Get the system user
-
sys_user = get_system_user
-
-
# Get the admin users
-
admins = User.with_role :system_admin
-
-
message_template = MessageTemplate.find_by(name: 'Support2')
-
if message_template
-
message_body = MessageTemplateMessageGenerator.new.generate(message_template, [templated_changed.name, templated_changed.subject])
-
-
# Send a message to the admins for this user organization
-
admins.each do |admin|
-
msg = Message.new
-
msg.organization = admin.organization
-
msg.user = sys_user
-
msg.to_user = admin
-
msg.subject = message_template.subject
-
msg.body = message_body
-
msg.priority_type = message_template.priority_type
-
msg.message_template = message_template
-
msg.save
-
end
-
end
-
-
else
-
raise RuntimeError, "Can't find Message Template with object_key #{object_key}"
-
end
-
-
end
-
-
def prepare
-
Rails.logger.info "Executing MessageTemplateInformerJob at #{Time.now.to_s} for Template #{object_key}"
-
end
-
-
def check
-
raise ArgumentError, "object_key can't be blank " if object_key.blank?
-
end
-
-
def initialize(object_key)
-
super
-
self.object_key = object_key
-
end
-
-
end
-
#------------------------------------------------------------------------------
-
#
-
# AssetUpdateJob
-
#
-
# Performs all updates on an asset
-
#
-
#------------------------------------------------------------------------------
-
1
class PolicyAssetSubtypeRuleDistributerJob < Job
-
-
1
attr_accessor :policy_asset_subtype_rules
-
1
attr_accessor :is_mileage
-
-
1
def run
-
44
PolicyAssetSubtypeRule.where(id: policy_asset_subtype_rules.split(',')).each do |policy_asset_subtype_rule|
-
2
policy_asset_subtype_rule.send(:apply_policy)
-
end
-
-
44
event_url = Rails.application.routes.url_helpers.policy_path Policy.find_by(parent_id: nil)
-
44
if is_mileage
-
msg = 'Parent policy distributed and assets updated for new policy mileage rules.'
-
else
-
44
msg = 'Parent policy distributed and assets updated for new policy rules.'
-
end
-
-
# Add a row into the activity table
-
44
ActivityLog.create({:organization_id =>Grantor.first.id, :user_id => User.find_by(first_name: 'system').id, :item_type => "Policy Asset Subtype Rule Distributor", :activity => msg, :activity_time => Time.now})
-
-
policy_notification = Notification.create(text: msg, link: event_url, notifiable_type: 'Organization', notifiable_id: Grantor.first.id)
-
UserNotification.create(user: User.find_by(first_name: 'system'), notification: policy_notification)
-
-
-
end
-
-
1
def prepare
-
44
Rails.logger.debug "Executing PolicyAssetSubtypeRuleDistributerJob at #{Time.now.to_s} for all assets"
-
end
-
-
1
def check
-
44
raise ArgumentError, "policy asset subtype rules can't be blank " if policy_asset_subtype_rules.nil?
-
end
-
-
1
def initialize(rule_ids, is_mileage=false)
-
44
super
-
44
self.policy_asset_subtype_rules = rule_ids
-
44
self.is_mileage = is_mileage
-
end
-
-
end
-
#------------------------------------------------------------------------------
-
#
-
# AssetUpdateJob
-
#
-
# Performs all updates on an asset
-
#
-
#------------------------------------------------------------------------------
-
class PolicyDistributerJob < Job
-
-
attr_accessor :policy_distributer_proxy
-
attr_accessor :creator
-
-
def run
-
-
Policy.where(parent_id: policy_distributer_proxy.policy.id).each do |p|
-
p.policy_asset_subtype_rules.each do |subtype_rule|
-
parent_rules = subtype_rule.min_allowable_policy_values
-
-
subtype_rule.update(subtype_rule.attributes.slice(*parent_rules.stringify_keys.keys).merge(parent_rules.stringify_keys){|key, oldval, newval| [oldval, newval].max})
-
end
-
-
if policy_distributer_proxy.apply_policies.to_i == 1
-
# Rip through the organizations assets, creating a job for each type requested
-
assets = Asset.operational.where(organization_id: p.organization_id)
-
assets.find_each do |a|
-
typed_asset = Asset.get_typed_asset(a)
-
typed_asset.update_methods.each do |m|
-
begin
-
typed_asset.send(m, false) #dont save until all updates have run
-
rescue Exception => e
-
Rails.logger.warn e.message
-
end
-
end
-
typed_asset.save
-
end
-
end
-
end
-
-
msg = 'Parent policy has been distributed.'
-
-
# Add a row into the activity table
-
ActivityLog.create({:organization_id =>policy_distributer_proxy.policy.organization_id, :user_id => creator.id, :item_type => "Policy Distributor", :activity => msg, :activity_time => Time.now})
-
-
event_url = Rails.application.routes.url_helpers.policy_path policy_distributer_proxy.policy
-
policy_notification = Notification.create(text: msg, link: event_url, notifiable_type: 'Organization', notifiable_id: policy_distributer_proxy.policy.organization_id)
-
UserNotification.create(user: creator, notification: policy_notification)
-
-
end
-
-
def prepare
-
Rails.logger.debug "Executing PolicyDistributerJob at #{Time.now.to_s} for all assets"
-
end
-
-
def check
-
raise ArgumentError, "policy distributer proxy can't be blank " if policy_distributer_proxy.nil?
-
raise ArgumentError, "creator can't be blank " if creator.nil?
-
end
-
-
def initialize(policy_distributer_proxy, creator)
-
super
-
self.policy_distributer_proxy = policy_distributer_proxy
-
self.creator = creator
-
end
-
-
end
-
#------------------------------------------------------------------------------
-
#
-
# SendMessageAsEmailJob
-
#
-
# Sends a message as an email
-
#
-
#------------------------------------------------------------------------------
-
1
class SendMessageAsEmailJob < Job
-
-
1
attr_accessor :object_key
-
-
1
def run
-
4
message = Message.find_by_object_key(object_key)
-
4
if message
-
2
UserMailer.email_message(message).deliver
-
else
-
2
raise RuntimeError, "Can't find Message with object_key #{object_key}"
-
end
-
end
-
-
1
def prepare
-
4
Rails.logger.debug "Executing SendMessageAsEmailJob at #{Time.now.to_s} for Message #{object_key}"
-
end
-
-
1
def check
-
4
raise ArgumentError, "object_key can't be blank " if object_key.blank?
-
end
-
-
1
def initialize(object_key)
-
6
super
-
6
self.object_key = object_key
-
end
-
-
end
-
#------------------------------------------------------------------------------
-
#
-
# SessionCacheCleanupJob.rb
-
#
-
# Checks cached session data that has expired and removes it from the cache
-
#
-
#------------------------------------------------------------------------------
-
class SessionCacheCleanupJob < ActivityJob
-
-
def run
-
-
now = Time.now
-
key = "000000:#{TransamController::ACTIVE_SESSION_LIST_CACHE_VAR}"
-
session_list = Rails.cache.fetch(key)
-
session_list.keys.each do |s|
-
if session_list[s][:expire_time] < now
-
session_list.delete(s)
-
end
-
end
-
Rails.cache.fetch(key, :force => true, :expires_in => 1.week) { session_list }
-
-
-
# Add a row into the activity table
-
ActivityLog.create({:organization_id => Grantor.first.id, :user_id => User.find_by(first_name: 'system').id, :item_type => self.class.name, :activity => 'Executing Session Cache Cleanup', :activity_time => Time.now})
-
end
-
-
def prepare
-
Rails.logger.info "Executing SessionCacheCleanupJob at #{Time.now.to_s}"
-
end
-
-
def check
-
-
end
-
-
end
-
#------------------------------------------------------------------------------
-
#
-
# TaskReminderJob
-
#
-
# Searches for tasks that are due and sends a message reminding
-
# the user that the task is due
-
#
-
# This task is designed to run once per day, every day
-
#
-
#------------------------------------------------------------------------------
-
1
class TaskReminderJob < ActivityJob
-
-
1
include Rails.application.routes.url_helpers
-
-
1
def run
-
-
# Get the system user
-
5
sys_user = get_system_user
-
-
# Get the task status types to search for
-
5
task_statuses = Task.active_states
-
-
-
5
[7,1].each do |days_from_now|
-
# Get the list of tasks that are incomplete and due in days_from_now days
-
10
date_due = Date.today + days_from_now.days
-
-
10
tasks = Task.where('state IN (?) AND send_reminder = ? AND complete_by BETWEEN ? and ?', task_statuses, true, date_due.beginning_of_day, date_due.end_of_day)
-
10
Rails.logger.info "Found #{tasks.count} incomplete tasks that are due in #{days_from_now} day(s)."
-
-
10
message_template = MessageTemplate.find_by(name: 'Task1')
-
-
-
10
if message_template
-
10
msg_generator_service = MessageTemplateMessageGenerator.new
-
-
10
tasks.each do |task|
-
1
custom_fields = [task.subject, task.complete_by.strftime("%m/%d/%Y"),"<a href='#{user_task_path(task.assigned_to_user, task)}'>here</a>"]
-
1
message_body = msg_generator_service.generate(message_template, custom_fields)
-
-
# Send a message to the assigned user for each task
-
1
msg = Message.new
-
1
msg.organization = task.organization
-
1
msg.user = sys_user
-
1
msg.to_user = task.assigned_to_user
-
1
msg.subject = message_template.subject
-
1
msg.body = message_body
-
1
msg.priority_type = days_from_now < 2 ? PriorityType.find_by_name('High') : message_template.priority_type
-
1
msg.message_template = message_template
-
1
msg.active = message_template.active
-
1
msg.save
-
end
-
end
-
-
end
-
-
# Add a row into the activity table
-
# add to activity log for all admin users
-
5
User.with_role(:admin).pluck('DISTINCT organization_id').each do |org_id|
-
19
ActivityLog.create({:organization_id => org_id, :user_id => sys_user.id, :item_type => self.class.name, :activity => 'Sent reminders for tasks due', :activity_time => Time.now})
-
end
-
-
end
-
-
1
def prepare
-
1
Rails.logger.info "Executing TaskReminderJob at #{Time.now.to_s} for tasks due."
-
end
-
-
end
-
#------------------------------------------------------------------------------
-
#
-
# UploadProcessorJob
-
#
-
# Processes a spreadsheet uploaded by a user. The spreadsheet can contain new
-
# inventory or updates to existing inventory
-
#
-
#------------------------------------------------------------------------------
-
class UploadProcessorJob < Job
-
-
attr_accessor :object_key
-
-
def run
-
upload = Upload.find_by_object_key(object_key)
-
if upload
-
# Get the handler for the upload. This is defined via the file_content_type class
-
begin
-
klass = upload.file_content_type.class_name.constantize.new(upload)
-
if klass.can_process?
-
klass.execute
-
end
-
rescue Exception => e
-
Rails.logger.error e.message
-
raise RuntimeError.new "Processing failed for Upload #{object_key}"
-
end
-
-
# Add a row into the activity table
-
ActivityLog.create({:organization_id => upload.organization_id, :user_id => User.find_by(first_name: 'system').id, :item_type => self.class.name, :activity => "#{upload.file_content_type} #{upload.file_status_type}.", :activity_time => Time.now})
-
-
event_url = Rails.application.routes.url_helpers.upload_path(upload)
-
upload_notification = Notification.create!(text: "#{upload.file_content_type} #{upload.file_status_type}.", link: event_url)
-
if upload.organization_id.present?
-
upload_notification.notifiable_type = 'Organization'
-
upload_notification.notifiable_id = upload.organization_id
-
end
-
UserNotification.create!(user: upload.user, notification: upload_notification)
-
-
else
-
raise RuntimeError, "Can't find upload with object_key #{object_key}"
-
end
-
end
-
-
def prepare
-
Rails.logger.debug "Executing UploadProcessorJob at #{Time.now.to_s} for Upload #{object_key}"
-
end
-
-
def check
-
raise ArgumentError, "object_key can't be blank " if object_key.blank?
-
end
-
-
def initialize(object_key)
-
super
-
self.object_key = object_key
-
end
-
-
end
-
class DeviseMailer < Devise::Mailer
-
-
def reset_password_instructions(record, token, opts={})
-
opts[:subject] = MessageTemplate.find_by(name: 'User2').subject
-
-
super
-
end
-
-
end
-
class DeviseMailerPreview < ActionMailer::Preview
-
def reset_password_instructions
-
DeviseMailer.reset_password_instructions(User.first, "faketoken", {})
-
end
-
end
-
class UserMailerPreview < ActionMailer::Preview
-
def email_message
-
UserMailer.email_message(Message.first || Message.new)
-
end
-
-
def send_email_on_user_creation
-
UserMailer.send_email_on_user_creation(User.first)
-
end
-
end
-
1
class UserMailer < ActionMailer::Base
-
-
# Default sender account set in application.yml
-
1
default from: ENV["SYSTEM_SEND_FROM_ADDRESS"]
-
-
1
def email_message(mesg)
-
2
@message = mesg
-
2
mail(:to => @message.to_user.email, :subject => @message.subject)
-
2
@message.update(email_status: Message::EMAIL_STATUS_SENT) if Rails.application.config.action_mailer.perform_deliveries
-
end
-
-
1
def send_email_on_user_creation(created_user)
-
1
@user = created_user
-
1
mail(:to => created_user.email, :subject => MessageTemplate.find_by(name: 'User1').subject)
-
end
-
-
end
-
module Abilities
-
class Admin
-
include CanCan::Ability
-
-
def initialize(user)
-
-
can :manage, :all
-
-
SystemConfig.transam_module_names.each do |mod|
-
ability = "Abilities::Admin#{mod.classify}Ability".constantize.new(user) rescue nil
-
-
self.merge ability if ability.present?
-
end
-
end
-
end
-
end
-
module Abilities
-
class AdminCoreAbility
-
include CanCan::Ability
-
-
def initialize(user, organization_ids=[])
-
-
cannot [:update], Activity do |a|
-
(a.system_activity == true)
-
end
-
cannot :destroy, Activity do |a|
-
(a.system_activity == true)
-
end
-
cannot :create, Activity
-
-
end
-
end
-
end
-
module Abilities
-
class AssetManager
-
include CanCan::Ability
-
-
def initialize(user)
-
-
# Only allow users to dispose of assets that are disposable and that they
-
# own
-
can :dispose, TransamAssetRecord do |a|
-
(DispositionUpdateEvent.asset_event_type.try(:active) && user.viewable_organization_ids.include?(a.organization_id))
-
end
-
-
can :manage, EarlyDispositionRequestUpdateEvent do |ae|
-
ae.asset_event_type.try(:active)
-
end
-
-
can :create, DispositionUpdateEvent
-
-
cannot :create, DispositionUpdateEvent do |ae|
-
!(DispositionUpdateEvent.asset_event_type.try(:active) && user.viewable_organization_ids.include?(ae.transam_asset.organization_id))
-
end
-
-
cannot :create, EarlyDispositionRequestUpdateEvent do |ae|
-
!ae.send(Rails.application.config.asset_base_class_name.underscore).try(:eligible_for_early_disposition_request?)
-
end
-
-
end
-
end
-
end
-
module Abilities
-
class Authorized
-
include CanCan::Ability
-
-
def initialize(user)
-
-
# view everything except the activity logs
-
can :read, :all
-
-
-
SystemConfig.transam_module_names.each do |mod|
-
ability = "Abilities::Authorized#{mod.classify}Ability".constantize.new(user) rescue nil
-
-
self.merge ability if ability.present?
-
end
-
-
#-------------------------------------------------------------------------
-
# Comments
-
#-------------------------------------------------------------------------
-
# comments for assets only if user can update asset
-
can :create, Comment do |c|
-
if c.commentable_type == 'Asset'
-
user.organization_ids.include? c.commentable.organization.id
-
else
-
true
-
end
-
end
-
-
# Can manage them if you are the owner
-
can [:update, :destroy], Comment do |c|
-
c.created_by_id == user.id
-
end
-
#-------------------------------------------------------------------------
-
# Documents
-
#-------------------------------------------------------------------------
-
# documents for assets only if user can update asset
-
can :create, Document do |d|
-
if d.documentable_type == 'Asset'
-
user.organization_ids.include? d.documentable.organization.id
-
else
-
true
-
end
-
end
-
-
# Can manage them if you are the owner
-
can [:update, :destroy], Document do |d|
-
d.created_by_id == user.id
-
end
-
#-------------------------------------------------------------------------
-
# Images
-
#-------------------------------------------------------------------------
-
# images for assets only if user can update asset
-
can :create, Image do |d|
-
if d.imagable_type == 'Asset'
-
user.organization_ids.include? d.imagable.organization.id
-
else
-
true
-
end
-
end
-
-
# Can manage them if you are the owner
-
can [:update, :destroy], Image do |d|
-
d.created_by_id == user.id
-
end
-
-
-
-
-
-
end
-
end
-
end
-
module Abilities
-
class AuthorizedActivityLogAbility
-
include CanCan::Ability
-
-
def initialize(user, organization_ids=[])
-
-
cannot :read, ActivityLog
-
-
end
-
end
-
end
-
module Abilities
-
class AuthorizedAssetAbility
-
include CanCan::Ability
-
-
def initialize(user, organization_ids=[])
-
-
if organization_ids.empty?
-
organization_ids = user.organization_ids
-
end
-
-
#-------------------------------------------------------------------------
-
# Assets
-
#-------------------------------------------------------------------------
-
# Create
-
can :create, [Asset]
-
-
# update assets for any organization in their list
-
can :update, Asset, :organization_id => organization_ids
-
# Prevent updating or removing assets that have been previously disposed
-
cannot [:update, :destroy], Asset do |a|
-
a.try(:disposed?)
-
end
-
-
-
# Create
-
can :create, [TransamAssetRecord]
-
-
# update assets for any organization in their list
-
can :update, TransamAssetRecord, :organization_id => organization_ids
-
# Prevent updating or removing assets that have been previously disposed
-
cannot [:update, :destroy], TransamAssetRecord do |a|
-
a.try(:disposed?)
-
end
-
-
end
-
end
-
end
-
module Abilities
-
class AuthorizedAssetEventAbility
-
include CanCan::Ability
-
-
def initialize(user, organization_ids=[])
-
-
if organization_ids.empty?
-
organization_ids = user.organization_ids
-
end
-
-
#-------------------------------------------------------------------------
-
# Asset Events
-
#-------------------------------------------------------------------------
-
# Can manage asset events if the asset is owned by their organization
-
can :manage, AssetEvent do |ae|
-
ae.asset_event_type.try(:active) && organization_ids.include?(ae.send(Rails.application.config.asset_base_class_name.underscore).try(:organization_id))
-
end
-
-
cannot :create, DispositionUpdateEvent do |ae|
-
!ae.send(Rails.application.config.asset_base_class_name.underscore).try(:disposable?,false)
-
end
-
-
-
cannot :create, EarlyDispositionRequestUpdateEvent do |ae|
-
!ae.send(Rails.application.config.asset_base_class_name.underscore).try(:eligible_for_early_disposition_request?)
-
end
-
-
cannot [:approve, :reject, :approve_via_transfer], EarlyDispositionRequestUpdateEvent
-
-
cannot [:update, :destroy], EarlyDispositionRequestUpdateEvent do |ae|
-
ae.state != 'new'
-
end
-
-
end
-
end
-
end
-
module Abilities
-
class AuthorizedAssetGroupAbility
-
include CanCan::Ability
-
-
def initialize(user, organization_ids=[])
-
-
if organization_ids.empty?
-
organization_ids = user.organization_ids
-
end
-
-
# Can manage asset groups if the group is owned by their organiation
-
can :manage, AssetGroup, :organization_id => organization_ids
-
-
end
-
end
-
end
-
module Abilities
-
class AuthorizedCoreAbility
-
include CanCan::Ability
-
-
def initialize(user, organization_ids=[])
-
-
if organization_ids.empty?
-
organization_ids = user.organization_ids
-
end
-
-
['ActivityLog', 'Asset', 'AssetEvent', 'AssetGroup', 'Issue', 'Message', 'SavedSearch', 'SystemConfig', 'Task', 'TransamWorkflow', 'Upload', 'User', 'UserOrganizationFilter', 'Vendor', 'SavedQuery'].each do |c|
-
ability = "Abilities::Authorized#{c}Ability".constantize.new(user, organization_ids)
-
-
self.merge ability if ability.present?
-
end
-
-
end
-
end
-
end
-
module Abilities
-
class AuthorizedIssueAbility
-
include CanCan::Ability
-
-
def initialize(user, organization_ids=[])
-
-
#-------------------------------------------------------------------------
-
# Issues
-
#-------------------------------------------------------------------------
-
# A user can manage issues if they created them
-
can :manage, Issue do |issue|
-
user.id == issue.created_by_id
-
end
-
-
end
-
end
-
end
-
module Abilities
-
class AuthorizedMessageAbility
-
include CanCan::Ability
-
-
def initialize(user, organization_ids=[])
-
-
#-------------------------------------------------------------------------
-
# Messages
-
#-------------------------------------------------------------------------
-
# Create
-
can :create, [Message]
-
-
end
-
end
-
end
-
module Abilities
-
class AuthorizedSavedQueryAbility
-
include CanCan::Ability
-
-
def initialize(user, organization_ids=[])
-
-
can [:create, :show, :export, :query, :export_unsaved, :clone, :save_as], SavedQuery
-
-
can [:update, :destroy], SavedQuery do |query|
-
user.id == query.created_by_user_id
-
end
-
-
end
-
end
-
end
-
module Abilities
-
class AuthorizedSavedSearchAbility
-
include CanCan::Ability
-
-
def initialize(user, organization_ids=[])
-
-
#-------------------------------------------------------------------------
-
# Issues
-
#-------------------------------------------------------------------------
-
# A user can manage issues if they created them
-
can :manage, SavedSearch do |search|
-
user.id == search.user_id
-
end
-
-
end
-
end
-
end
-
module Abilities
-
class AuthorizedSystemConfigAbility
-
include CanCan::Ability
-
-
def initialize(user, organization_ids=[])
-
-
cannot :manage, SystemConfig
-
-
end
-
end
-
end
-
module Abilities
-
class AuthorizedTaskAbility
-
include CanCan::Ability
-
-
def initialize(user, organization_ids=[])
-
-
#-------------------------------------------------------------------------
-
# Tasks
-
#-------------------------------------------------------------------------
-
# Everyone can create a task
-
can :create, [Task]
-
-
# Can manage them if you are the owner
-
can :manage, Task do |t|
-
t.user_id == user.id
-
end
-
# can update them if you are the recipient
-
can [:update, :fire_workflow_event], Task do |t|
-
t.assigned_to_user_id == user.id
-
end
-
-
end
-
end
-
end
-
module Abilities
-
class AuthorizedTransamWorkflowAbility
-
include CanCan::Ability
-
-
def initialize(user, organization_ids=[])
-
-
if organization_ids.empty?
-
organization_ids = user.organization_ids
-
end
-
-
TransamWorkflow.implementors.each do |klass|
-
klass.transam_workflow_transitions.each do |transition|
-
if transition[:can].present?
-
if transition[:can].is_a?(Hash)
-
-
can transition[:event_name].to_sym, klass do |klass_instance|
-
transition[:can].map{|k,v| klass_instance.state.to_s == k.to_s && klass_instance.send(v, user)}.any?
-
end
-
else
-
can transition[:event_name].to_sym, klass do |klass_instance|
-
klass_instance.send(transition[:can], user)
-
end
-
end
-
-
-
-
else
-
can transition[:event_name].to_sym, klass
-
end
-
end
-
end
-
-
end
-
end
-
end
-
module Abilities
-
class AuthorizedUploadAbility
-
include CanCan::Ability
-
-
def initialize(user, organization_ids=[])
-
-
if organization_ids.empty?
-
organization_ids = user.organization_ids
-
end
-
-
#-------------------------------------------------------------------------
-
# Uploads
-
#-------------------------------------------------------------------------
-
can :create, [Upload]
-
-
# can update and remove uploads as long as they are not being processed
-
# checks multi org spreadsheets based on assets linked to uploads
-
can [:update, :destroy], Upload do |u|
-
asset_ids = Asset.where('organization_id IN (?) AND upload_id IS NOT NULL', organization_ids).pluck(:upload_id)
-
((asset_ids.include? u.id) || (organization_ids.include? u.organization_id)) && ([1,3,4,5].include? u.file_status_type_id)
-
end
-
-
can :read, FileContentType
-
-
end
-
end
-
end
-
module Abilities
-
class AuthorizedUserAbility
-
include CanCan::Ability
-
-
def initialize(user, organization_ids=[])
-
-
#-------------------------------------------------------------------------
-
# Users
-
#-------------------------------------------------------------------------
-
# can update their own user record as long as it is not from dotGrants
-
can [:update], User do |usr|
-
user.id == usr.id
-
end
-
# can everything else if they are the current user
-
can [:change_password, :update_password, :settings, :profile_photo], User do |usr|
-
user.id == usr.id
-
end
-
-
end
-
end
-
end
-
module Abilities
-
class AuthorizedUserOrganizationFilterAbility
-
include CanCan::Ability
-
-
def initialize(user, organization_ids=[])
-
-
can :manage, UserOrganizationFilter
-
-
end
-
end
-
end
-
module Abilities
-
class AuthorizedVendorAbility
-
include CanCan::Ability
-
-
def initialize(user, organization_ids=[])
-
-
#-------------------------------------------------------------------------
-
# Vendors
-
#-------------------------------------------------------------------------
-
can :manage, Vendor do |v|
-
v.organization_id == user.organization_id
-
end
-
-
end
-
end
-
end
-
module Abilities
-
class Guest
-
include CanCan::Ability
-
-
def initialize(user)
-
-
# view everything except the activity logs
-
can :read, :all
-
-
-
SystemConfig.transam_module_names.each do |mod|
-
ability = "Abilities::Guest#{mod.classify}Ability".constantize.new(user) rescue nil
-
-
self.merge ability if ability.present?
-
end
-
-
end
-
end
-
end
-
module Abilities
-
class GuestCoreAbility
-
include CanCan::Ability
-
-
def initialize(user)
-
-
self.merge Abilities::AuthorizedActivityLogAbility.new(user)
-
self.merge Abilities::AuthorizedUserAbility.new(user)
-
self.merge Abilities::AuthorizedIssueAbility.new(user)
-
self.merge Abilities::AuthorizedSavedSearchAbility.new(user)
-
self.merge Abilities::AuthorizedSavedQueryAbility.new(user)
-
self.merge Abilities::AuthorizedUserOrganizationFilterAbility.new(user)
-
end
-
end
-
end
-
module Abilities
-
class Manager
-
include CanCan::Ability
-
-
def initialize(user)
-
-
SystemConfig.transam_module_names.each do |mod|
-
ability = "Abilities::Manager#{mod.classify}Ability".constantize.new(user) rescue nil
-
-
self.merge ability if ability.present?
-
end
-
-
-
-
-
end
-
end
-
end
-
module Abilities
-
class ManagerAssetAbility
-
include CanCan::Ability
-
-
def initialize(user)
-
-
# Only allow users to dispose of assets that are disposable and that they
-
# own
-
can :dispose, Asset do |a|
-
(DispositionUpdateEvent.asset_event_type.try(:active) && a.disposable?(true) && user.organization_ids.include?(a.organization_id))
-
end
-
-
can :destroy, Asset
-
-
-
can :dispose, TransamAssetRecord do |a|
-
(DispositionUpdateEvent.asset_event_type.try(:active) && a.disposable?(true) && user.organization_ids.include?(a.organization_id))
-
end
-
-
#can :destroy, TransamAssetRecord
-
-
-
end
-
end
-
end
-
module Abilities
-
class ManagerAssetEventAbility
-
include CanCan::Ability
-
-
def initialize(user)
-
-
#-------------------------------------------------------------------------
-
# Asset Events
-
#-------------------------------------------------------------------------
-
# managers can manage asset events if the asset's organization is in their list
-
can :manage, AssetEvent do |ae|
-
ae.asset_event_type.try(:active) && user.organization_ids.include?(ae.send(Rails.application.config.asset_base_class_name.underscore).try(:organization_id))
-
end
-
-
can :manage, EarlyDispositionRequestUpdateEvent do |ae|
-
ae.asset_event_type.try(:active)
-
end
-
-
cannot :create, DispositionUpdateEvent do |ae|
-
!ae.send(Rails.application.config.asset_base_class_name.underscore).try(:disposable?,true)
-
end
-
-
cannot :create, EarlyDispositionRequestUpdateEvent do |ae|
-
!ae.send(Rails.application.config.asset_base_class_name.underscore).try(:eligible_for_early_disposition_request?)
-
end
-
-
end
-
end
-
end
-
module Abilities
-
class ManagerCoreAbility
-
include CanCan::Ability
-
-
def initialize(user)
-
-
['Asset', 'AssetEvent', 'Organization', 'Policy', 'Role', 'Upload', 'User'].each do |c|
-
ability = "Abilities::Manager#{c}Ability".constantize.new(user)
-
-
self.merge ability if ability.present?
-
end
-
-
end
-
end
-
end
-
module Abilities
-
class ManagerOrganizationAbility
-
include CanCan::Ability
-
-
def initialize(user)
-
-
# can update organization records
-
can :update, Organization
-
-
end
-
end
-
end
-
module Abilities
-
class ManagerPolicyAbility
-
include CanCan::Ability
-
-
def initialize(user)
-
-
#-------------------------------------------------------------------------
-
# Policies
-
#-------------------------------------------------------------------------
-
# create new policies
-
can :create, Policy
-
-
# Policies can be updated if they belong to the organization
-
can :update, Policy do |p|
-
user.organization_ids.include? p.organization_id
-
end
-
-
# Only grantors can create new rules and then only for a top-level policy
-
can :create_rules, Policy do |p|
-
p.parent.nil? and user.organization.organization_type.class_name == "Grantor"
-
end
-
-
# can remove policies if they are not current and are in their organizations list
-
can :destroy, Policy do |p|
-
p.active == false and user.organization_ids.include? p.organization_id
-
end
-
-
end
-
end
-
end
-
module Abilities
-
class ManagerRoleAbility
-
include CanCan::Ability
-
-
def initialize(user)
-
-
# if the super manager role exists only super managers can assign all roles (minus admin), managers just can assign users
-
# otherwise managers can assign
-
if Role.find_by(name: 'super_manager').present?
-
can :assign, Role do |r|
-
['user'].include? r.name
-
end
-
else
-
can :assign, Role do |r|
-
r.name != "admin"
-
end
-
end
-
-
end
-
end
-
end
-
module Abilities
-
class ManagerUploadAbility
-
include CanCan::Ability
-
-
def initialize(user)
-
-
#-------------------------------------------------------------------------
-
# Uploads
-
#-------------------------------------------------------------------------
-
-
# bpt user can do bulk functions for anybody
-
can [:update, :destroy], Upload do |u|
-
[1,3,4,5].include? u.file_status_type_id
-
end
-
-
end
-
end
-
end
-
module Abilities
-
class ManagerUserAbility
-
include CanCan::Ability
-
-
def initialize(user)
-
-
can [:create, :update, :authorizations], User
-
-
end
-
end
-
end
-
module Abilities
-
class SuperManager
-
include CanCan::Ability
-
-
def initialize(user)
-
-
SystemConfig.transam_module_names.each do |mod|
-
ability = "Abilities::SuperManager#{mod.classify}Ability".constantize.new(user) rescue nil
-
-
self.merge ability if ability.present?
-
-
end
-
-
# BPT super manager can add comments to funding sources
-
can :create, Comment
-
-
#-------------------------------------------------------------------------
-
# Documents
-
#-------------------------------------------------------------------------
-
# documents for assets only if user can update asset
-
can :create, Document do |d|
-
if d.documentable_type == 'Asset'
-
user.organization_ids.include? d.documentable.organization.id
-
else
-
true
-
end
-
end
-
-
-
-
end
-
end
-
end
-
module Abilities
-
class SuperManagerCoreAbility
-
include CanCan::Ability
-
-
def initialize(user, organization_ids=[])
-
-
can :assign, Role do |r|
-
r.name != "admin"
-
end
-
-
# can manage SavedQuery
-
can :manage, SavedQuery
-
end
-
end
-
end
-
1
class Ability
-
1
include CanCan::Ability
-
-
# Define abilities for the passed in user here
-
#
-
# all actions must map to one of
-
# :read,
-
# :create,
-
# :update and
-
# :destroy
-
#
-
1
def initialize(user)
-
# Admins can do everything
-
139
if user.has_role? :admin
-
135
can :manage, :all
-
4
elsif user.has_role? :user
-
2
can :manage, :all
-
2
elsif user.has_role? :manager
-
2
can :manage, :all
-
else
-
can :read, :all
-
end
-
end
-
end
-
#-------------------------------------------------------------------------------
-
#
-
# Activity
-
#
-
# Represents a process that is run on a schedule like a cron job. Activities can
-
# include system level processes, audits, and other activities.
-
#
-
#-------------------------------------------------------------------------------
-
1
class Activity < ActiveRecord::Base
-
-
# Include the object key mixin
-
1
include TransamObjectKey
-
-
#-----------------------------------------------------------------------------
-
# Callbacks
-
#-----------------------------------------------------------------------------
-
1
after_initialize :set_defaults
-
-
#-----------------------------------------------------------------------------
-
# Associations
-
#-----------------------------------------------------------------------------
-
# Every activity has a frequency_type eg hour, minute, day, etc.
-
1
belongs_to :frequency_type
-
-
#-----------------------------------------------------------------------------
-
# Scopes
-
#-----------------------------------------------------------------------------
-
1
scope :active, -> { where(:active => true) }
-
-
#-----------------------------------------------------------------------------
-
# Validations
-
#-----------------------------------------------------------------------------
-
1
validates :name, :presence => true
-
1
validates :description, :presence => true
-
1
validates_inclusion_of :show_in_dashboard, :in => [true, false]
-
1
validates_inclusion_of :system_activity, :in => [true, false]
-
#validates :start_date, :allow_nil => true
-
#validates :end_date, :allow_nil => true
-
1
validates :frequency_quantity, :presence => true, :numericality => {:only_integer => true, :greater_than_or_equal_to => 1}
-
1
validates :frequency_type, :presence => true
-
1
validates :job_name, :presence => true
-
1
validates_inclusion_of :active, :in => [true, false]
-
-
# List of allowable form param hash keys
-
1
FORM_PARAMS = [
-
:name,
-
:description,
-
:show_in_dashboard,
-
:system_activity,
-
:start_date,
-
:end_date,
-
:frequency_quantity,
-
:frequency_type_id,
-
:execution_time,
-
:job_name,
-
:active
-
]
-
-
#-----------------------------------------------------------------------------
-
# Class Methods
-
#-----------------------------------------------------------------------------
-
1
def self.allowable_params
-
1
FORM_PARAMS
-
end
-
-
#-----------------------------------------------------------------------------
-
# Instance Methods
-
#-----------------------------------------------------------------------------
-
-
#-----------------------------------------------------------------------------
-
# Returns true if this job is operational on the current day. If the start_date
-
# and end date are set the current date should be in the range, if they are not
-
# set this always returns true
-
#-----------------------------------------------------------------------------
-
1
def operational?
-
8
if start_date.blank?
-
4
true
-
else
-
4
if end_date.blank? or end_date < start_date
-
2
false
-
else
-
2
today = Date.today
-
2
(start_date <= today and end_date >= today)
-
end
-
end
-
end
-
-
1
def to_s
-
1
name
-
end
-
-
# Returns a string description of this activities execution schedule
-
1
def schedule
-
4
"#{frequency_quantity} #{frequency_type.name.pluralize} at #{execution_time}"
-
end
-
-
# Used by clockwork to schedule how frequently this event should be run
-
# Should be the intended number of seconds between executions
-
1
def frequency
-
frequency_quantity.send(frequency_type.name.pluralize)
-
end
-
-
# Used by clockwork to specify when to trigger an event
-
1
def at
-
execution_time
-
end
-
-
# Returns the job that will be executed
-
1
def job
-
1
job_name.constantize.new({
-
:context => self
-
})
-
end
-
#-----------------------------------------------------------------------------
-
# Protected Methods
-
#-----------------------------------------------------------------------------
-
1
protected
-
-
# Set resonable defaults for a new organization
-
1
def set_defaults
-
37
self.active = self.active.nil? ? true : self.active
-
37
self.show_in_dashboard = self.show_in_dashboard.nil? ? true : self.show_in_dashboard
-
37
self.system_activity = self.system_activity.nil? ? false : self.system_activity
-
37
self.frequency_quantity ||= 1
-
end
-
-
end
-
1
class ActivityLog < ActiveRecord::Base
-
-
1
validates :organization_id, :item_type, :user_id, :activity, :activity_time, :presence => true
-
-
1
belongs_to :organization
-
1
belongs_to :user, -> { unscope(where: :active) }
-
-
1
def to_s
-
1
item_type
-
end
-
-
end
-
1
class ArchivedFiscalYear < ActiveRecord::Base
-
-
1
include FiscalYear
-
-
1
belongs_to :organization
-
-
1
validates_uniqueness_of :fy_year, :scope => :organization
-
-
2
default_scope { order(:fy_year) }
-
-
# if force update, will unarchive if FY Year is found to be already archived or vice versa
-
1
def self.archive(organization_id, fy_year, force_update=false)
-
archived = ArchivedFiscalYear.find_by(organization_id: organization_id, fy_year: fy_year)
-
if archived.present? && force_update
-
archived.destroy
-
elsif archived.nil?
-
ArchivedFiscalYear.create(organization_id: organization_id, fy_year: fy_year)
-
else
-
return nil
-
end
-
end
-
-
1
def self.unarchive(organization_id, fy_year, force_update=false)
-
archived = ArchivedFiscalYear.find_by(organization_id: organization_id, fy_year: fy_year)
-
if archived.nil? && force_update
-
ArchivedFiscalYear.create(organization_id: organization_id, fy_year: fy_year)
-
elsif archived.present?
-
archived.destroy
-
else
-
return nil
-
end
-
end
-
-
end
-
#-------------------------------------------------------------------------------
-
#
-
# Asset
-
#
-
# Base class for all assets. This class represents a generic asset, subclasses represent concrete
-
# asset types.
-
#
-
#-------------------------------------------------------------------------------
-
1
class Asset < ActiveRecord::Base
-
-
1
OBJECT_CACHE_EXPIRE_SECONDS = Rails.application.config.object_cache_expire_seconds
-
-
#-----------------------------------------------------------------------------
-
# Behaviors
-
#-----------------------------------------------------------------------------
-
1
include TransamObjectKey
-
1
include TransamNumericSanitizers
-
1
include FiscalYear
-
-
#-----------------------------------------------------------------------------
-
# Callbacks
-
#-----------------------------------------------------------------------------
-
1
after_initialize :set_defaults
-
-
# Clean up any HABTM associations before the asset is destroyed
-
3
before_destroy { asset_groups.clear }
-
-
# Before the asset is updated we may need to update things like estimated
-
# replacement cost if they updated other things
-
1
before_update :before_update_callback, :except => :create
-
-
1
before_update :clear_cache
-
-
#-----------------------------------------------------------------------------
-
# Associations common to all asset types
-
#-----------------------------------------------------------------------------
-
-
1
has_one :transit_asset
-
-
# each asset belongs to a single organization
-
1
belongs_to :organization
-
-
# each asset has a single asset type
-
1
belongs_to :asset_type
-
-
# each asset has a single asset subtype
-
1
belongs_to :asset_subtype
-
-
# each asset has a reason why it is being replaced
-
1
belongs_to :replacement_reason_type
-
-
# each was puchased from a vendor
-
1
belongs_to :vendor
-
-
# each belongs to a single manufacturer
-
1
belongs_to :manufacturer
-
-
# an upload can be added by bulk updates - new inventory
-
1
belongs_to :upload
-
-
# each can belong to a parent
-
1
belongs_to :parent, :class_name => "Asset", :foreign_key => :parent_id
-
-
1
belongs_to :location, :class_name => "Asset", :foreign_key => :location_id
-
-
# Each asset has zero or more asset events. These are all events regardless of
-
# event type. Events are deleted when the asset is deleted
-
1
has_many :asset_events, :dependent => :destroy
-
-
# each asset has zero or more condition updates
-
64
has_many :condition_updates, -> {where :asset_event_type_id => ConditionUpdateEvent.asset_event_type.id }, :class_name => "ConditionUpdateEvent"
-
-
# each asset has zero or more scheduled replacement updates
-
9
has_many :schedule_replacement_updates, -> {where :asset_event_type_id => ScheduleReplacementUpdateEvent.asset_event_type.id }, :class_name => "ScheduleReplacementUpdateEvent"
-
-
# each asset has zero or more scheduled rehabilitation updates
-
9
has_many :schedule_rehabilitation_updates, -> {where :asset_event_type_id => ScheduleRehabilitationUpdateEvent.asset_event_type.id }, :class_name => "ScheduleRehabilitationUpdateEvent"
-
-
# each asset has zero or more recorded rehabilitation events
-
50
has_many :rehabilitation_updates, -> {where :asset_event_type_id => RehabilitationUpdateEvent.asset_event_type.id}, :class_name => "RehabilitationUpdateEvent"
-
-
# each asset has zero or more scheduled disposition updates
-
9
has_many :schedule_disposition_updates, -> {where :asset_event_type_id => ScheduleDispositionUpdateEvent.asset_event_type.id }, :class_name => "ScheduleDispositionUpdateEvent"
-
-
# each asset has zero or more service status updates
-
23
has_many :service_status_updates, -> {where :asset_event_type_id => ServiceStatusUpdateEvent.asset_event_type.id }, :class_name => "ServiceStatusUpdateEvent"
-
-
# each asset has zero or more disposition updates
-
7
has_many :disposition_updates, -> {where :asset_event_type_id => DispositionUpdateEvent.asset_event_type.id }, :class_name => "DispositionUpdateEvent"
-
-
# each asset has zero or more early disposition requests
-
26
has_many :early_disposition_requests, -> {where :asset_event_type_id => EarlyDispositionRequestUpdateEvent.asset_event_type.id }, :class_name => "EarlyDispositionRequestUpdateEvent"
-
-
# each asset has zero or more location updates.
-
3
has_many :location_updates, -> {where :asset_event_type_id => LocationUpdateEvent.asset_event_type.id }, :class_name => "LocationUpdateEvent"
-
-
# Each asset has zero or more images. Images are deleted when the asset is deleted
-
1
has_many :images, :as => :imagable, :dependent => :destroy
-
-
# Each asset has zero or more documents. Documents are deleted when the asset is deleted
-
1
has_many :documents, :as => :documentable, :dependent => :destroy
-
-
# Each asset has zero or more comments. Documents are deleted when the asset is deleted
-
1
has_many :comments, :as => :commentable, :dependent => :destroy
-
-
# Each asset has zero or more tasks. Tasks are deleted when the asset is deleted
-
1
has_many :tasks, :as => :taskable, :dependent => :destroy
-
-
# Each asset can have 0 or more dependents (parent-child relationships)
-
1
has_many :dependents, :class_name => 'Asset', :foreign_key => :parent_id, :dependent => :nullify
-
-
1
accepts_nested_attributes_for :dependents
-
-
# Facilities can have many vehicles stored on their premises
-
1
has_many :occupants, :class_name => 'Asset', :foreign_key => :location_id, :dependent => :nullify
-
-
# Each asset can be associated with 0 or more asset groups
-
1
has_and_belongs_to_many :asset_groups
-
-
# Each asset was created and updated by a user
-
1
belongs_to :creator, -> { unscope(where: :active) }, :class_name => "User", :foreign_key => :created_by_id
-
1
belongs_to :updator, -> { unscope(where: :active) }, :class_name => "User", :foreign_key => :updated_by_id
-
-
# Has been tagged by the user
-
1
has_many :asset_tags
-
1
has_many :users, :through => :asset_tags
-
-
# Validations on associations
-
1
validates :organization_id, :presence => true
-
1
validates :asset_type_id, :presence => true
-
1
validates :asset_subtype_id, :presence => true
-
1
validates :created_by_id, :presence => true
-
1
validates :manufacture_year, :presence => true, :numericality => {:only_integer => true, :greater_than_or_equal_to => 1900}
-
1
validates :expected_useful_life, :numericality => {:only_integer => true, :greater_than_or_equal_to => 0}, :presence => true
-
1
validates_inclusion_of :purchased_new, :in => [true, false]
-
1
validates :purchase_cost, :presence => true, :numericality => {:only_integer => true, :greater_than_or_equal_to => 0}
-
1
validates :purchase_date, :presence => true
-
1
validates :serial_number, uniqueness: {scope: :organization, message: "must be unique within an organization"}, allow_nil: true, allow_blank: true
-
-
#-----------------------------------------------------------------------------
-
# Attributes common to all asset types
-
#-----------------------------------------------------------------------------
-
-
# Asset tags must be unique within an organization
-
1
validates_uniqueness_of :asset_tag, :scope => :organization
-
-
# Validations on core attributes
-
1
validates :asset_tag, :presence => true, :length => { :maximum => 12 }
-
1
validate :object_key_is_not_asset_tag
-
-
#-----------------------------------------------------------------------------
-
# Attributes that are used to cache asset condition information.
-
# This set of attributes are updated when the asset condirtion or disposition is
-
# updated. Used for reporting only.
-
#-----------------------------------------------------------------------------
-
-
# The last reported condition type for the asset
-
1
belongs_to :reported_condition_type, :class_name => "ConditionType", :foreign_key => :reported_condition_type_id
-
-
# The last estimated condition type for the asset
-
1
belongs_to :estimated_condition_type, :class_name => "ConditionType", :foreign_key => :estimated_condition_type_id
-
-
# The disposition type for the asset. Null if the asset is still operational
-
1
belongs_to :disposition_type
-
-
# The last reported disposition type for the asset
-
1
belongs_to :service_status_type
-
-
#-----------------------------------------------------------------------------
-
# Transient Attributes
-
#-----------------------------------------------------------------------------
-
1
attr_reader :vendor_name
-
1
attr_accessor :parent_name
-
-
#-----------------------------------------------------------------------------
-
# Scopes
-
#-----------------------------------------------------------------------------
-
-
# Returns a list of assets that have been disposed
-
1
scope :disposed, -> { where('assets.disposition_date IS NOT NULL') }
-
# Returns a list of assets that are still operational
-
623
scope :operational, -> { where('assets.disposition_date IS NULL AND assets.asset_tag != assets.object_key') }
-
# Returns a list of asset that operational and are marked as being in service
-
1
scope :in_service, -> { where('assets.disposition_date IS NULL AND assets.service_status_type_id = 1')}
-
-
# Returns a list of asset that in early replacement
-
1
scope :early_replacement, -> { where('policy_replacement_year is not NULL and scheduled_replacement_year is not NULL and scheduled_replacement_year < policy_replacement_year') }
-
#-----------------------------------------------------------------------------
-
# Lists. These lists are used by derived classes to make up lists of attributes
-
# that can be used for operations like full text search etc. Each derived class
-
# can add their own fields to the list
-
#-----------------------------------------------------------------------------
-
-
# List of fields which can be searched using a simple text-based search
-
1
SEARCHABLE_FIELDS = [
-
:object_key,
-
:asset_tag,
-
:external_id,
-
:description,
-
:manufacturer_model
-
]
-
-
# List of fields that should be nilled when a copy is made
-
1
CLEANSABLE_FIELDS = [
-
'object_key',
-
'asset_tag',
-
'external_id',
-
'policy_replacement_year',
-
'estimated_replacement_year',
-
'estimated_replacement_cost',
-
'scheduled_replacement_year',
-
'early_replacement_reason',
-
'scheduled_rehabilitation_year',
-
'scheduled_disposition_year',
-
'replacement_reason_type_id',
-
'in_backlog',
-
'reported_condition_type_id',
-
'reported_condition_rating',
-
'reported_condition_date',
-
'reported_mileage',
-
'estimated_condition_type_id',
-
'estimated_condition_rating',
-
'service_status_type_id',
-
'disposition_type_id',
-
'disposition_date'
-
]
-
-
1
UPDATE_METHODS = [
-
:update_sogr,
-
:update_service_status,
-
:update_condition,
-
:update_scheduled_replacement,
-
:update_scheduled_rehabilitation,
-
:update_scheduled_disposition,
-
:update_estimated_replacement_cost,
-
:update_scheduled_replacement_cost,
-
:update_location
-
]
-
-
# List of hash parameters allowed by the controller
-
1
FORM_PARAMS = [
-
:organization_id,
-
:asset_type_id,
-
:asset_subtype_id,
-
:asset_tag,
-
:external_id,
-
:manufacture_year,
-
:vendor_id,
-
:vendor_name,
-
:manufacturer_id,
-
:other_manufacturer,
-
:manufacturer_model,
-
:purchase_cost,
-
:purchase_date,
-
:purchased_new,
-
:warranty_date,
-
:in_service_date,
-
:policy_replacement_year,
-
:expected_useful_life,
-
:estimated_replacement_year,
-
:estimated_replacement_cost,
-
:scheduled_replacement_year,
-
:scheduled_rehabilitation_year,
-
:scheduled_disposition_year,
-
:replacement_reason_type_id,
-
:in_backlog,
-
:reported_condition_type_id,
-
:reported_condition_rating,
-
:reported_condition_date,
-
:estimated_condition_type_id,
-
:estimated_condition_rating,
-
:service_status_type_id,
-
:service_status_date,
-
:disposition_date,
-
:disposition_type_id,
-
:parent_id,
-
:parent_name,
-
:parent_key,
-
:location_id,
-
:superseded_by_id,
-
:weight,
-
:created_by_id,
-
:updated_by_id
-
]
-
-
#-----------------------------------------------------------------------------
-
#
-
# Class Methods
-
#
-
#-----------------------------------------------------------------------------
-
-
1
def self.decorates
-
decor = AssetDecorator.new(self.unscoped.ids)
-
decor.whichHierarchy(true)
-
return decor
-
end
-
-
# Returns an array of classes which are descendents of Asset, this includes classes
-
# that are both direct and in-direct assendents.
-
#
-
# Example
-
#
-
# class Truck < Asset
-
# end
-
#
-
# class PickupTruck < Truck
-
# end
-
#
-
# Asset.descendents returns [Truck, PickupTruck]
-
#
-
1
def self.descendents
-
ObjectSpace.each_object(Class).select { |klass| klass < self }
-
end
-
-
# Returns the list or allowable form parameters for this class
-
1
def self.allowable_params
-
FORM_PARAMS
-
end
-
-
# Factory method to return a strongly typed subclass of a new asset
-
# based on the asset subtype
-
1
def self.new_asset(asset_subtype, params={})
-
-
1
asset_class_name = asset_subtype.asset_type.class_name
-
1
asset = asset_class_name.constantize.new({:asset_subtype_id => asset_subtype.id, :asset_type_id => asset_subtype.asset_type.id})
-
1
return asset
-
-
end
-
-
1
def self.very_specific
-
if self.distinct.pluck(:asset_type_id).count == 1
-
self.first.asset_type.class_name.constantize.where(id: self.ids)
-
else
-
self
-
end
-
end
-
-
# Returns a typed version of an asset. Every asset has a type and this will
-
# return a specific asset type based on the AssetType attribute
-
1
def self.get_typed_asset(asset)
-
1199
if asset
-
1198
class_name = asset.asset_type.class_name
-
1198
klass = Object.const_get class_name
-
1198
o = klass.find(asset.id)
-
1198
return o
-
end
-
end
-
-
# Returns an array of asset event classes that this asset can process
-
1
def self.event_classes
-
4
a = []
-
# Use reflection to return the list of has many associatiopns and filter those which are
-
# events
-
4
self.reflect_on_all_associations(:has_many).each do |assoc|
-
73
a << assoc.klass if assoc.class_name.end_with? 'UpdateEvent'
-
end
-
4
a
-
end
-
-
#-----------------------------------------------------------------------------
-
#
-
# Instance Methods
-
#
-
#-----------------------------------------------------------------------------
-
-
# Returns true if the user has tagged this asset
-
1
def tagged? user
-
users.include? user
-
end
-
-
# Tags this asset for the user
-
1
def tag user
-
unless tagged? user
-
users << user
-
end
-
end
-
-
# Returns true if the asset has been superseded
-
1
def superseded?
-
(self.superseded_by_id.present?)
-
end
-
-
# Returns the asset that replaced this asset if it has been replaced
-
1
def superseded_by
-
if self.superseded_by_id.present?
-
Asset.get_typed_asset(Asset.find(self.superseded_by_id))
-
else
-
nil
-
end
-
end
-
-
# Returns the asset that this asset replaced this asset if it has been replaced
-
1
def supersedes
-
asset = Asset.find_by(:superseded_by_id => self.id)
-
if asset.present?
-
Asset.get_typed_asset(asset)
-
else
-
nil
-
end
-
end
-
-
# Render the asset as a JSON object -- overrides the default json encoding
-
1
def as_json(options={})
-
if options[:is_super]
-
super(options)
-
else
-
json = {
-
:id => self.id,
-
:object_key => self.object_key,
-
:asset_tag => self.asset_tag,
-
:external_id => self.external_id,
-
-
:organization_id => self.organization.to_s,
-
:asset_type_id=> self.asset_type.to_s,
-
:asset_subtype_id => self.asset_subtype.to_s,
-
-
:parent_id => self.parent.to_s,
-
:location_id => self.location.to_s,
-
:name => self.name,
-
:description => self.description,
-
-
:service_status_type_id => self.service_status_type.present? ? self.service_status_type.code : nil,
-
:age => self.age,
-
:reported_condition_rating => self.reported_condition_rating,
-
-
:scheduled_rehabilitation_year => self.scheduled_rehabilitation_year.present? ? fiscal_year(self.scheduled_rehabilitation_year) : nil,
-
:scheduled_replacement_year => self.scheduled_replacement_year.present? ? fiscal_year(self.scheduled_replacement_year) : nil,
-
-
:manufacturer_id => self.manufacturer.present? ? self.manufacturer.to_s : nil,
-
:manufacture_year => self.manufacture_year,
-
-
:purchase_cost => self.purchase_cost,
-
:purchase_date => self.purchase_date,
-
:purchased_new => self.purchased_new,
-
:warranty_date => self.warranty_date,
-
:in_service_date => self.in_service_date,
-
:disposition_date => self.disposition_date,
-
:vendor_id => self.vendor.present? ? self.vendor.to_s : nil,
-
-
:created_at => self.created_at,
-
:updated_at => self.updated_at,
-
-
:tasks => self.tasks.active.count,
-
:comments => self.comments.count,
-
:documents => self.documents.count,
-
:photos => self.images.count,
-
-
:tagged => self.tagged?(options[:user]) ? 1 : 0
-
}
-
-
if options[:include_early_disposition]
-
json[:early_disposition_notes] = self.early_disposition_notes
-
json[:early_disposition_event_object_key] = self.early_disposition_requests.last.try(:object_key)
-
end
-
-
if self.respond_to? :book_value
-
a = Asset.get_typed_asset self
-
json.merge! a.depreciable_as_json
-
end
-
json
-
end
-
end
-
-
1
def to_node(selected=nil)
-
node_options = {
-
:text => self.asset_tag,
-
:href => "/inventory/#{self.object_key}",
-
:nodes => self.dependents.distinct.map{|d| d.to_node(selected)}
-
}
-
-
# expands everything above selected and then selects selected
-
if selected
-
if selected.object_key == self.object_key
-
node_options[:state] ||= {}
-
node_options[:state][:selected] = true
-
end
-
end
-
-
node_options
-
end
-
-
# get the greatest grand parent of asset or return self if no parents
-
1
def top_parent
-
top_parent = self
-
while top_parent.parent.present?
-
top_parent = top_parent.parent
-
end
-
-
return top_parent
-
end
-
-
1
def level
-
level = 1 # asset itself is a level
-
-
current_asset = self
-
while current_asset.parent.present?
-
level += 1
-
current_asset = current_asset.parent
-
end
-
-
return level
-
end
-
-
1
def relatives
-
relatives = []
-
relatives = self.dependents
-
-
x = self.dependents
-
i = 0
-
while i < x.count
-
relatives << x[i]
-
i += 1
-
x[i].dependents.each{|xx| x << xx}
-
end
-
-
relatives
-
end
-
-
# Override to_s to return a reasonable default
-
1
def to_s
-
192
"#{asset_subtype.name}: #{asset_tag}"
-
end
-
-
# Override the getter for vendor name
-
1
def vendor_name
-
3
vendor.name unless vendor.nil?
-
end
-
-
1
def parent_name
-
3
parent.to_s unless parent.nil?
-
end
-
-
1
def parent_key=(object_key)
-
self.parent = Asset.find_by_object_key(object_key)
-
end
-
1
def parent_key
-
2
parent.object_key if parent
-
end
-
-
# Returns true if the asset has one or more tasks that are open
-
1
def needs_attention?
-
(tasks.where('state IN (?)', Task.active_states).count > 0)
-
end
-
-
# Returns true if the asset is in service based on the last reported status update
-
1
def in_service?
-
service_status_type_id == ServiceStatusType.find_by(:code => 'I')
-
end
-
-
# Instantiate an asset event of the appropriate type.
-
1
def build_typed_event(asset_event_type_class)
-
# Could also add: raise ArgumentError 'Asset Must be strongly typed' unless is_typed?
-
-
# DO NOT cast to concrete type. Want to enforce that client has a concrete asset
-
3
unless self.class.event_classes.include? asset_event_type_class
-
1
raise ArgumentError, 'Invalid Asset Event Type'
-
end
-
2
asset_event_type_class.new(:asset => self)
-
end
-
-
# Returns true if an asset has been disposed. This is the canonical method for checking
-
# if an asset has been disposed. Always use this method rather than checking the
-
# attributes as the data model might change
-
1
def disposed?
-
35
disposition_date.present?
-
end
-
-
# Returns true if the asset exists, i.e. has an in_service_date. If using this
-
# method, make sure the context is well defined, for example, a building could
-
# be under construction but is not yet in service, similary, a bus could have
-
# been purchased but has not been placed into service.
-
1
def exists?
-
(in_service_date.present? and disposed? == false)
-
end
-
-
# Returns true if the asset can be disposed in the next planning cycle,
-
# false otherwise
-
1
def disposable?( include_early_disposal_request_approved_via_transfer = false)
-
return false if disposed?
-
# otherwise check the policy year and see if it is less than or equal to
-
# the current planning year
-
return false if policy_replacement_year.blank?
-
-
if policy_replacement_year <= current_planning_year_year
-
# After ESL disposal
-
true
-
else
-
# Prior ESL disposal request
-
last_request = early_disposition_requests.last
-
if include_early_disposal_request_approved_via_transfer
-
last_request.try(:is_approved?)
-
else
-
last_request.try(:is_unconditional_approved?)
-
end
-
end
-
end
-
-
# Returns true if the asset can be requested for early disposal
-
1
def eligible_for_early_disposition_request?
-
return false if disposed?
-
# otherwise check the policy year and see if it is less than or equal to
-
# the current planning year
-
return false if policy_replacement_year.blank?
-
-
if policy_replacement_year <= current_planning_year_year
-
# Eligible for after ESL disposal
-
false
-
else
-
# Prior ESL disposal request
-
last_request = early_disposition_requests.last
-
# No previous request or was rejected
-
!last_request || last_request.try(:is_rejected?)
-
end
-
end
-
-
# Return early disposition reason
-
# (this method is needed to show the reason in asset table)
-
1
def early_disposition_notes
-
early_disposition_requests.active.last.try(:comments) || ""
-
end
-
-
1
def replacement_by_policy?
-
69
true # all assets in core are in replacement cycle. To plan and/or make exceptions to normal schedule, see CPT.
-
end
-
-
1
def replacement_pinned?
-
2
false # all assets can be locked into place to prevent sched replacement year changes but by default none are locked
-
end
-
-
# Returns true if an asset is scheduled for disposition
-
1
def scheduled_for_disposition?
-
(scheduled_disposition_year.present? and disposed? == false)
-
end
-
-
# Returns true if an asset is scheduled for rehabilitation
-
1
def scheduled_for_rehabilitation?
-
(scheduled_rehabilitation_year.present? and disposed? == false)
-
end
-
-
# Returns true if the asset is of the specified class or has the specified class as
-
# and ancestor (superclass).
-
#
-
# usage: a.type_of? type
-
# where type can be one of:
-
# a symbol e.g :vehicle
-
# a class name eg Vehicle
-
# a string eg "vehicle"
-
#
-
5
def type_of?(type)
-
begin
-
4
self.class.ancestors.include?(type.to_s.classify.constantize)
-
rescue
-
1
false
-
end
-
end
-
-
#-----------------------------------------------------------------------------
-
# Override numeric setters to remove any extraneous formats from the number
-
# strings eg $, etc.
-
#-----------------------------------------------------------------------------
-
1
def manufacture_year=(num)
-
657
self[:manufacture_year] = sanitize_to_int(num)
-
end
-
-
1
def purchase_cost=(num)
-
326
self[:purchase_cost] = sanitize_to_int(num)
-
end
-
-
1
def expected_useful_life=(num)
-
330
self[:expected_useful_life] = sanitize_to_int(num)
-
end
-
#-----------------------------------------------------------------------------
-
-
-
# Returns true if this asset participates in one or more events
-
1
def has_events?
-
event_classes.size > 0
-
end
-
-
# Returns an array of asset event classes that this asset can process
-
1
def event_classes
-
# get a typed version of the asset
-
asset = is_typed? ? self : Asset.get_typed_asset(self)
-
asset.class.event_classes
-
end
-
-
# Returns the initial cost of the asset. Derived classes should override this to
-
# handle asset-class specific caluclations where needed
-
1
def cost
-
# get a typed version of the asset and return its value
-
2
asset = is_typed? ? self : Asset.get_typed_asset(self)
-
2
return asset.cost unless asset.nil?
-
end
-
-
# returns true if the asset instance is strongly typed, i.e., a concrete class
-
# false otherwise.
-
# true
-
1
def is_typed?
-
15
self.class.to_s == asset_type.class_name
-
end
-
-
# default name for an asset
-
# sub classes should override this with a class-specific name
-
1
def name
-
470
return "#{asset_subtype.name} - #{asset_tag}"
-
end
-
-
# returns the number of months an asset has been in service
-
1
def months_in_service(on_date=Date.today)
-
43
if in_service_date.nil?
-
0
-
else
-
43
(on_date.year * 12 + on_date.month) - (in_service_date.year * 12 + in_service_date.month)
-
end
-
end
-
-
# returns the number of months since the asset was rehabilitated
-
1
def months_since_rehabilitation(on_date=Date.today)
-
if last_rehabilitation_date.nil?
-
0
-
else
-
(on_date.year * 12 + on_date.month) - (last_rehabilitation_date.year * 12 + last_rehabilitation_date.month)
-
end
-
end
-
-
# returns the number of years since the asset was placed in service.
-
1
def age(on_date=Date.today)
-
43
age_in_years = months_in_service(on_date)/12.0
-
43
[(age_in_years).floor, 0].max
-
end
-
-
# returns the number of years since the asset was owned. It can't be less than 0
-
# years_owned is currently calculated based off fiscal year
-
1
def years_owned(on_date=Date.today)
-
[fiscal_year_on_date(on_date) - fiscal_year_on_date(purchase_date), 0].max
-
end
-
-
# returns the number of years the asset is in service. It can't be less than 0
-
# years_in_service is currently calculated based off fiscal year
-
1
def years_in_service(on_date=Date.today)
-
[fiscal_year_on_date(on_date,current_depreciation_date) - fiscal_year_on_date(in_service_date), 0].max
-
end
-
-
# Returns the fiscal year that the asset was placed in service
-
1
def in_service_fiscal_year
-
fiscal_year_on_date(in_service_date) unless in_service_date.nil?
-
end
-
-
# returns the list of events associated with this asset ordered by date, newest first
-
1
def history
-
3
AssetEvent.unscoped.where('asset_id = ?', id).order('event_date DESC, created_at DESC')
-
end
-
-
1
def estimated_rehabilitation_cost(on_date = Date.today)
-
calculate_estimated_rehabilitation_cost(on_date)
-
end
-
-
# Returns the total amount spent rehabilitating the asset
-
1
def rehabilitation_cost
-
if rehabilitation_updates.empty?
-
0
-
else
-
rehabilitation_updates.map(&:cost)
-
end
-
end
-
-
#-----------------------------------------------------------------------------
-
# Return the policy analyzer for this asset. If a policy is not provided the
-
# default policy for the asset is used
-
#-----------------------------------------------------------------------------
-
1
def policy_analyzer(policy_to_use=nil)
-
172
if policy_to_use.blank?
-
172
policy_to_use = policy
-
end
-
172
policy_analyzer = Rails.application.config.policy_analyzer.constantize.new(self, policy_to_use)
-
end
-
#-----------------------------------------------------------------------------
-
# returns the the organizations's policy that governs the replacement of this
-
# asset. This needs to upcast the organization type to a class that owns assets
-
#-----------------------------------------------------------------------------
-
1
def policy
-
194
Organization.get_typed_organization(organization).get_policy
-
end
-
-
#-----------------------------------------------------------------------------
-
# Record that the asset has been disposed. This updates the dispostion date
-
# and the disposition_type attribute on the master record. It also sets the
-
# ServiceStatusType to 'disposed'
-
#
-
# If the user removed the disposition event, the master record is reset by
-
# removing the disposition type, disposition date and setting the service
-
# status back to the last known status if on exists
-
1
def record_disposition
-
2
Rails.logger.info "Recording final disposition for asset = #{object_key}"
-
-
# Make sure we are working with a concrete asset class
-
2
asset = is_typed? ? self : Asset.get_typed_asset(self)
-
-
2
unless asset.new_record?
-
2
unless asset.disposition_updates.empty?
-
2
event = asset.disposition_updates.last
-
2
asset.disposition_date = event.event_date
-
2
asset.disposition_type = event.disposition_type
-
-
# Set the service status to disposed
-
2
asset.service_status_type = ServiceStatusType.find_by(:code => 'D')
-
else
-
# The user is undo-ing the disposition update
-
asset.disposition_type = nil
-
asset.disposition_date = nil
-
# Set the service status to the last known service status
-
if asset.service_status_updates.empty?
-
# Set to Uknown
-
asset.service_status_type = ServiceStatusType.find_by(:code => 'U')
-
else
-
asset.service_status_type = asset.service_status_updates.last.service_status_type
-
end
-
end
-
# save changes to this asset
-
2
asset.save(:validate => false)
-
end
-
end
-
-
# Determines if the asset has been disposes and if a type is passed if it has been disposed by that type
-
1
def disposed disposition_type=nil
-
asset = is_typed? ? self : Asset.get_typed_asset(self)
-
-
is_disposed = asset.service_status_type == ServiceStatusType.find_by(:code => 'D')
-
-
unless disposition_type.nil?
-
is_disposed = is_disposed && asset.disposition_type_id == disposition_type.id
-
end
-
-
return is_disposed
-
end
-
-
# Forces an update of an assets location. This performs an update on the record.
-
1
def update_location(save_asset = true)
-
-
1
Rails.logger.debug "Updating the recorded location for asset = #{object_key}"
-
-
1
unless new_record? or disposed?
-
1
if location_updates.empty?
-
1
self.location_id = nil
-
1
self.location_comments = nil
-
else
-
event = location_updates.last
-
self.location_id = event.parent_id
-
self.location_comments = event.comments
-
end
-
# save changes to this asset
-
1
save if save_asset
-
end
-
end
-
-
1
def update_rehabilitation(save_asset = true)
-
1
Rails.logger.debug "Updating the recorded rehabilitation for asset = #{object_key}"
-
-
# Make sure we are working with a concrete asset class
-
1
asset = is_typed? ? self : Asset.get_typed_asset(self)
-
-
# can't do this if it is a new record as none of the IDs would be set
-
1
unless asset.new_record? or disposed?
-
1
if asset.rehabilitation_updates.empty?
-
asset.last_rehabilitation_date = nil
-
else
-
1
asset.last_rehabilitation_date = asset.rehabilitation_updates.last.event_date
-
1
asset.scheduled_rehabilitation_year = nil
-
end
-
# save changes to this asset
-
1
asset.save(:validate => false) if save_asset
-
end
-
end
-
-
# Forces an update of an assets service status. This performs an update on the record
-
1
def update_service_status(save_asset = true)
-
4
Rails.logger.debug "Updating service status for asset = #{object_key}"
-
-
# Make sure we are working with a concrete asset class
-
4
asset = is_typed? ? self : Asset.get_typed_asset(self)
-
-
# can't do this if it is a new record as none of the IDs would be set
-
4
unless asset.new_record? or disposed?
-
4
if asset.service_status_updates.empty?
-
2
asset.service_status_type = nil
-
2
asset.service_status_date = nil
-
else
-
2
event = asset.service_status_updates.last
-
2
asset.service_status_date = event.event_date
-
2
asset.service_status_type = event.service_status_type
-
end
-
# save changes to this asset
-
4
asset.save(:validate => false) if save_asset
-
end
-
end
-
-
# Forces an update of an assets reported condition. This performs an update on the record.
-
1
def update_condition(save_asset = true)
-
-
4
Rails.logger.debug "Updating condition for asset = #{object_key}"
-
-
# can't do this if it is a new record as none of the IDs would be set
-
4
unless new_record? or disposed?
-
4
if self.dependents.count > 0
-
calc_from_dependents = self.policy_analyzer.get_condition_rollup_calculation_type.class_name.constantize.new.calculate(self)
-
self.reported_condition_date = self.dependents.order(:reported_condition_date).pluck(:reported_condition_date).last
-
self.reported_condition_rating = calc_from_dependents
-
self.reported_condition_type = ConditionType.from_rating(calc_from_dependents)
-
-
# save changes to this asset
-
save(:validate => false) if save_asset
-
-
else
-
4
if condition_updates.empty?
-
1
self.reported_condition_date = nil
-
1
self.reported_condition_rating = nil
-
1
self.reported_condition_type = ConditionType.find_by(:name => "Unknown")
-
else
-
3
event = condition_updates.last
-
3
self.reported_condition_date = event.event_date
-
3
self.reported_condition_rating = event.assessed_rating
-
3
self.reported_condition_type = ConditionType.from_rating(event.assessed_rating)
-
end
-
-
# save changes to this asset
-
4
save(:validate => false) if save_asset
-
-
4
affected_parent = self.parent
-
4
while affected_parent.present?
-
affected_parent.update_condition
-
affected_parent = affected_parent.parent
-
end
-
end
-
-
-
end
-
-
end
-
-
# Forces an update of an assets scheduled replacement. This performs an update on the record.
-
1
def update_scheduled_replacement(save_asset = true)
-
-
3
Rails.logger.debug "Updating the scheduled replacement year for asset = #{object_key}"
-
-
3
unless new_record? or disposed?
-
3
unless schedule_replacement_updates.empty?
-
1
event = schedule_replacement_updates.last
-
1
self.scheduled_replacement_year = event.replacement_year unless event.replacement_year.nil?
-
1
self.replacement_reason_type_id = event.replacement_reason_type_id unless event.replacement_reason_type_id.nil?
-
end
-
# save changes to this asset
-
3
save(:validate => false) if save_asset
-
end
-
end
-
-
# Forces an update of an assets scheduled replacement. This performs an update on the record.
-
1
def update_scheduled_rehabilitation(save_asset = true)
-
-
3
Rails.logger.debug "Updating the scheduled rehabilitation year for asset = #{object_key}"
-
-
3
unless new_record? or disposed?
-
3
if schedule_rehabilitation_updates.empty?
-
2
self.scheduled_rehabilitation_year = nil
-
else
-
1
event = schedule_rehabilitation_updates.last
-
1
self.scheduled_rehabilitation_year = event.rebuild_year
-
end
-
# save changes to this asset
-
3
save(:validate => false) if save_asset
-
end
-
end
-
-
# Forces an update of an assets scheduled disposition
-
1
def update_scheduled_disposition(save_asset = true)
-
-
3
Rails.logger.debug "Updating the scheduled disposition for asset = #{object_key}"
-
-
3
unless new_record? or disposed?
-
3
if schedule_disposition_updates.empty?
-
2
self.scheduled_disposition_year = nil
-
else
-
1
event = schedule_disposition_updates.last
-
1
self.scheduled_disposition_year = event.disposition_year
-
end
-
# save changes to this asset
-
3
save(:validate => false) if save_asset
-
end
-
end
-
-
-
1
def update_estimated_replacement_cost(save_asset = true)
-
-
2
return if disposed?
-
-
2
if self.policy_replacement_year < current_planning_year_year
-
1
start_date = start_of_fiscal_year(scheduled_replacement_year)
-
else
-
start_date = start_of_fiscal_year(policy_replacement_year)
-
end
-
1
self.estimated_replacement_cost = calculate_estimated_replacement_cost(start_date)
-
# save changes to this asset
-
1
save(:validate => false) if save_asset
-
-
end
-
-
#-----------------------------------------------------------------------------
-
# Calculates and stores the scheduled replacement for the asset based on the
-
# scheduled replacement year
-
#-----------------------------------------------------------------------------
-
1
def update_scheduled_replacement_cost(save_asset = true)
-
-
1
return if disposed?
-
-
1
if self.scheduled_replacement_year.blank?
-
start_date = start_of_fiscal_year(policy_replacement_year)
-
else
-
1
start_date = start_of_fiscal_year(scheduled_replacement_year)
-
end
-
1
self.scheduled_replacement_cost = calculate_estimated_replacement_cost(start_date)
-
-
# save changes to this asset
-
1
save(:validate => false) if save_asset
-
-
end
-
-
#-----------------------------------------------------------------------------
-
# Calculates and returns the estimated replacement cost of this asset on the
-
# current date or another date if given
-
#-----------------------------------------------------------------------------
-
1
def calculate_estimated_replacement_cost(on_date=nil)
-
-
2
return if disposed?
-
-
# Make sure we are working with a concrete asset class
-
2
asset = is_typed? ? self : Asset.get_typed_asset(self)
-
-
# Get the calculator class from the policy analyzer
-
2
class_name = asset.policy_analyzer.get_replacement_cost_calculation_type.class_name
-
# create an instance of this class and call the method
-
2
calculator_instance = class_name.constantize.new
-
2
(calculator_instance.calculate_on_date(asset, on_date)+0.5).to_i #round
-
-
end
-
-
#-----------------------------------------------------------------------------
-
# Calculates and returns the estimated rehabilitaiton cost of this asset on the
-
# current date or another date if given
-
#-----------------------------------------------------------------------------
-
1
def calculate_estimated_rehabilitation_cost(on_date=nil)
-
-
return if disposed?
-
-
# Make sure we are working with a concrete asset class
-
asset = is_typed? ? self : Asset.get_typed_asset(self)
-
-
# Get the cost from the policy
-
cost = asset.policy_analyzer.get_total_rehabilitation_cost
-
# TODO - we should use the cost calculator to project this cost to the
-
# on_date
-
cost
-
end
-
-
# calculate the year that the asset will need replacing based on
-
# a policy
-
1
def calculate_replacement_year(policy = nil)
-
-
1
return if disposed?
-
-
# Make sure we are working with a concrete asset class
-
asset = is_typed? ? self : Asset.get_typed_asset(self)
-
# Get the calculator class from the policy analyzer
-
class_name = asset.policy_analyzer.get_service_life_calculation_type.class_name
-
# determine the last year that the vehicle is viable based on the policy
-
last_year_for_service = calculate(asset, class_name)
-
# the asset will need replacing the next year
-
last_year_for_service + 1
-
-
end
-
-
# calculate the estimated year that the asset will need replacing based on
-
# a policy
-
1
def calculate_estimated_replacement_year(policy = nil)
-
-
1
return if disposed?
-
-
# Make sure we are working with a concrete asset class
-
asset = is_typed? ? self : Asset.get_typed_asset(self)
-
-
# Get the calculator class from the policy analyzer
-
class_name = asset.policy_analyzer.get_condition_estimation_type.class_name
-
# estimate the last year that the asset will be servicable
-
last_year_for_service = calculate(asset, class_name, 'last_servicable_year')
-
# the asset will need replacing the next year
-
last_year_for_service + 1
-
-
end
-
-
# if scheduled replacement year is earlier than the policy year
-
1
def is_early_replacement?
-
4
policy_replacement_year && scheduled_replacement_year && scheduled_replacement_year < policy_replacement_year
-
end
-
-
1
def update_early_replacement_reason(reason = nil)
-
if is_early_replacement?
-
self.early_replacement_reason = reason
-
else
-
self.early_replacement_reason = nil
-
end
-
end
-
-
1
def formatted_early_replacement_reason
-
1
early_replacement_reason.blank? ? '(Reason not provided)' : early_replacement_reason
-
end
-
-
# Creates a duplicate that has all asset-specific attributes nilled
-
1
def copy(cleanse = true)
-
4
a = dup
-
4
a.cleanse if cleanse
-
4
a
-
end
-
-
1
def searchable_fields
-
672
SEARCHABLE_FIELDS
-
end
-
-
1
def update_methods
-
2
a = []
-
2
a << super rescue nil # Must call super in case an engine includes updates above Asset
-
2
UPDATE_METHODS.each do |method|
-
18
a << method
-
end
-
2
a.flatten
-
end
-
-
# nils out all fields identified to be cleansed
-
1
def cleanse
-
2
cleansable_fields.each do |field|
-
42
send(:"#{field}=", nil) # Rather than set methods directly, delegate to setters. This supports aliased attributes
-
end
-
end
-
-
# Update the SOGR for an asset
-
1
def update_sogr(save_asset = true, policy = nil)
-
5
unless disposed?
-
4
update_asset_state(save_asset, policy)
-
end
-
end
-
# Update the replacement costs for an asset
-
1
def update_sogr_costs
-
unless disposed?
-
update_asset_state(policy)
-
end
-
end
-
-
# External method for managing an object's local cache
-
1
def cache_clear(key)
-
delete_cached_object(key)
-
end
-
1
def cache_clear_all
-
clear_cache
-
end
-
-
# This is soemthing that should be handled by each asset type individually so we will need to ensure it is
-
# implemented for each type. The model here is the same that organizations use.
-
1
def transfer new_organization_id
-
typed_asset = Asset.get_typed_asset(self)
-
-
return typed_asset.transfer new_organization_id
-
end
-
-
-
-
#-----------------------------------------------------------------------------
-
#
-
# Protected Methods
-
#
-
#-----------------------------------------------------------------------------
-
1
protected
-
-
# Callback to update the asset when things change
-
1
def before_update_callback
-
-
65
Rails.logger.debug "In before_save_callback"
-
-
65
return unless self.replacement_by_policy? || self.replacement_pinned?
-
-
# Get the policy analyzer
-
65
this_policy_analyzer = self.policy_analyzer
-
-
# If the policy replacement year changes we need to check to see if the asset
-
# is in backlog and update the scheduled replacement year to the first planning
-
# year
-
65
if self.changes.include? "policy_replacement_year"
-
2
check_early_replacement = true
-
2
Rails.logger.debug "New policy_replacement_year = #{self.policy_replacement_year}"
-
-
2
if self.policy_replacement_year < current_planning_year_year
-
2
self.scheduled_replacement_year = current_planning_year_year
-
2
self.in_backlog = true
-
2
start_date = start_of_fiscal_year(scheduled_replacement_year)
-
else
-
self.in_backlog = false
-
start_date = start_of_fiscal_year(policy_replacement_year)
-
end
-
# Update the estimated replacement costs
-
2
Rails.logger.debug "Start Date = #{start_date}"
-
2
class_name = this_policy_analyzer.get_replacement_cost_calculation_type.class_name
-
2
calculator_instance = class_name.constantize.new
-
2
self.estimated_replacement_cost = (calculator_instance.calculate_on_date(self, start_date)+0.5).to_i
-
2
Rails.logger.debug "estimated_replacement_cost = #{self.estimated_replacement_cost}"
-
end
-
-
65
if self.changes.include? "scheduled_replacement_year"
-
3
check_early_replacement = true
-
3
Rails.logger.debug "New scheduled_replacement_year = #{self.scheduled_replacement_year}"
-
# Get the calculator class from the policy analyzer
-
3
class_name = this_policy_analyzer.get_replacement_cost_calculation_type.class_name
-
3
calculator_instance = class_name.constantize.new
-
3
start_date = start_of_fiscal_year(scheduled_replacement_year) unless scheduled_replacement_year.blank?
-
3
Rails.logger.debug "Start Date = #{start_date}"
-
3
self.scheduled_replacement_cost = (calculator_instance.calculate_on_date(self, start_date)+0.5).to_i
-
end
-
-
65
self.early_replacement_reason = nil if check_early_replacement && !is_early_replacement?
-
-
65
true
-
end
-
-
# Return an object that has been cached against this asset
-
1
def get_cached_object(key)
-
Rails.cache.read(get_cache_key(key))
-
end
-
-
1
def delete_cached_object(key)
-
6435
Rails.cache.delete(get_cache_key(key))
-
end
-
-
# Cache an object against the asset
-
1
def cache_object(key, obj, expires_in = OBJECT_CACHE_EXPIRE_SECONDS)
-
Rails.cache.write(get_cache_key(key), obj, :expires_in => expires_in)
-
end
-
-
# Cache key for this asset
-
1
def get_cache_key(key)
-
6435
"#{object_key}:#{key}"
-
end
-
-
# Cache key for this asset
-
1
def clear_cache
-
6435
attributes.each { |attribute| delete_cached_object(attribute[0]) }
-
# clear cache for other cached objects that are not attributes
-
# hard-coded temporarily
-
65
delete_cached_object('policy_rule')
-
end
-
-
# updates the calculated values of an asset
-
1
def update_asset_state(save_asset = true, policy = nil)
-
-
4
return unless self.replacement_by_policy? || self.replacement_pinned?
-
-
4
Rails.logger.debug "Updating SOGR for asset = #{object_key}"
-
-
4
if disposed?
-
Rails.logger.debug "Asset #{object_key} is disposed"
-
end
-
-
# Make sure we are working with a concrete asset class
-
4
asset = is_typed? ? self : Asset.get_typed_asset(self)
-
-
# Get the policy analyzer
-
4
policy_analyzer = asset.policy_analyzer
-
-
# Use policy to update service life values
-
4
asset.update_service_life
-
-
# returns the year in which the asset should be replaced based on the policy and asset
-
# characteristics
-
4
begin
-
# store old policy replacement year for use later
-
4
old_policy_replacement_year = asset.policy_replacement_year
-
-
# see what metric we are using to determine the service life of the asset
-
4
class_name = policy_analyzer.get_service_life_calculation_type.class_name
-
2
asset.policy_replacement_year = calculate(asset, class_name)
-
-
2
if asset.scheduled_replacement_year.nil? or asset.scheduled_replacement_year == old_policy_replacement_year
-
2
Rails.logger.debug "Setting scheduled replacement year to #{asset.policy_replacement_year}"
-
2
asset.scheduled_replacement_year = asset.policy_replacement_year unless self.replacement_pinned?
-
2
asset.in_backlog = false
-
end
-
# If the asset is in backlog set the scheduled year to the current FY year
-
2
if asset.scheduled_replacement_year < current_planning_year_year
-
2
Rails.logger.debug "Asset is in backlog. Setting scheduled replacement year to #{current_planning_year_year}"
-
2
asset.scheduled_replacement_year = current_planning_year_year
-
2
asset.in_backlog = true
-
end
-
rescue Exception => e
-
2
Rails.logger.warn e.message
-
end
-
-
# Estimate the year that the asset will need replacing amd the estimated
-
# condition of the asset
-
4
begin
-
4
class_name = policy_analyzer.get_condition_estimation_type.class_name
-
4
asset.estimated_replacement_year = calculate(asset, class_name, 'last_servicable_year')
-
-
4
asset.estimated_condition_rating = calculate(asset, class_name)
-
4
asset.estimated_condition_type = ConditionType.from_rating(asset.estimated_condition_rating)
-
rescue Exception => e
-
Rails.logger.warn e.message
-
end
-
-
# Check for rehabilitation policy events
-
4
begin
-
# Use the calculator to calculate the policy rehabilitation fiscal year
-
4
calculator = RehabilitationYearCalculator.new
-
4
asset.policy_rehabilitation_year = calculator.calculate(asset)
-
rescue Exception => e
-
Rails.logger.warn e.message
-
end
-
-
# save changes to this asset
-
4
asset.save(:validate => false) if save_asset
-
end
-
-
1
def update_service_life
-
# Get the policy analyzer
-
4
policy_analyzer = self.policy_analyzer
-
-
4
self.purchased_new ?
-
self.expected_useful_life = policy_analyzer.get_min_service_life_months :
-
self.expected_useful_life = policy_analyzer.get_min_used_purchase_service_life_months
-
end
-
-
-
1
def cleansable_fields
-
2
CLEANSABLE_FIELDS
-
end
-
-
# Set reasonable defaults for a new asset
-
1
def set_defaults
-
1924
self.purchase_date ||= Date.today
-
1924
self.in_service_date ||= self.purchase_date
-
1924
self.manufacture_year ||= Date.today.year
-
1924
self.purchased_new = self.purchased_new.nil? ? true : self.purchased_new
-
end
-
-
#-----------------------------------------------------------------------------
-
#
-
# Private Methods
-
#
-
#-----------------------------------------------------------------------------
-
1
private
-
-
# Calls a calculate method on a Calculator class to perform a condition or cost calculation
-
# for the asset. The method name defaults to x.calculate(asset) but other methods
-
# with the same signature can be passed in
-
11
def calculate(asset, class_name, target_method = 'calculate')
-
begin
-
10
Rails.logger.debug "#{class_name}, #{target_method}"
-
# create an instance of this class and call the method
-
10
calculator_instance = class_name.constantize.new
-
10
Rails.logger.debug "Instance created #{calculator_instance}"
-
10
method_object = calculator_instance.method(target_method)
-
10
Rails.logger.debug "Instance method created #{method_object}"
-
10
method_object.call(asset)
-
rescue Exception => e
-
Rails.logger.error e.message
-
raise RuntimeError.new "#{class_name} calculation failed for asset #{asset.object_key} and policy #{policy.name}"
-
end
-
end
-
-
1
def object_key_is_not_asset_tag
-
362
unless self.asset_tag.nil? || self.object_key.nil?
-
362
if self.asset_tag == self.object_key
-
@errors.add(:asset_tag, "should not be the same as the asset id")
-
end
-
end
-
end
-
-
end
-
#
-
# Abstract class for events. All implementation specific events are derived from
-
# this
-
#
-
1
class AssetEvent < ActiveRecord::Base
-
-
# Include the object key mixin
-
1
include TransamObjectKey
-
# Include the numeric sanitizers mixin
-
1
include TransamNumericSanitizers
-
# Include the fiscal year mixin
-
1
include FiscalYear
-
-
#------------------------------------------------------------------------------
-
# Overrides
-
#------------------------------------------------------------------------------
-
-
-
# Callbacks
-
1
after_initialize :set_defaults
-
1
before_create :set_base_transam_asset
-
-
# Associations
-
-
# Every event belongs to an asset
-
1
belongs_to :asset
-
1
belongs_to :transam_asset, polymorphic: true
-
1
belongs_to :base_transam_asset, class_name: 'TransamAsset'
-
# Every event is of a type
-
1
belongs_to :asset_event_type
-
# Assets can be associated with Uploads
-
1
belongs_to :upload
-
# Every event belongs to a creator
-
3
belongs_to :creator, -> { unscope(where: :active) }, :class_name => "User", :foreign_key => :created_by_id
-
1
belongs_to :updater, -> { unscope(where: :active) }, :class_name => "User", :foreign_key => :updated_by_id
-
-
#validates :transam_asset, :presence => true
-
1
validates_associated :transam_asset
-
1
validates :asset_event_type_id, :presence => true
-
1
validates :event_date, :presence => true
-
1
validate :validate_event_date_with_purchase
-
-
# default scope
-
494
default_scope { order("asset_events.event_date, asset_events.created_at") }
-
# named scopes
-
-
# List of hash parameters allowed by the controller
-
1
FORM_PARAMS = [
-
:asset_id,
-
:asset_event_type_id,
-
:asset_type_id,
-
:event_date,
-
:comments
-
]
-
-
#------------------------------------------------------------------------------
-
#
-
# Class Methods
-
#
-
#------------------------------------------------------------------------------
-
-
1
def self.allowable_params
-
2
FORM_PARAMS
-
end
-
-
# Returns a typed version of itself. Every asset has a type and this will
-
# return a specific asset type based on the AssetType attribute
-
1
def self.as_typed_event(asset_event)
-
23
if asset_event
-
22
class_name = asset_event.asset_event_type.class_name
-
22
klass = Object.const_get class_name
-
22
o = klass.find(asset_event.id)
-
22
return o
-
end
-
end
-
-
#------------------------------------------------------------------------------
-
#
-
# Instance Methods
-
#
-
#------------------------------------------------------------------------------
-
-
# usually no conditions on can create but can be overridden by specific asset events
-
1
def can_update?
-
1
asset_event_type.active
-
end
-
-
# returns true if the organization instance is strongly typed, i.e., a concrete class
-
# false otherwise.
-
# true
-
1
def is_typed?
-
4
self.class.to_s == asset_event_type.class_name
-
end
-
-
# Return the update by coercing as a typed class and returning the appropriate
-
# update from it
-
1
def get_update
-
# get a typed version of the asset event and return its value
-
2
evt = is_typed? ? self : AssetEvent.as_typed_event(self)
-
2
return evt.get_update unless evt.nil?
-
end
-
#------------------------------------------------------------------------------
-
#
-
# Traversal Methods
-
#
-
#------------------------------------------------------------------------------
-
-
# Get the chronologically next event on this event's asset of the same type as the caller
-
# If one already exists for the same event_date, return the last created
-
# If none exists, returns nil
-
1
def next_event_of_type
-
event = transam_asset.asset_events
-
.where('asset_event_type_id = ?', self.asset_event_type_id)
-
.where('event_date > ? OR (event_date = ? AND created_at > ?)', self.event_date, self.event_date, (self.new_record? ? Time.current : self.created_at )) # Define a window that backs up to this event
-
.where('object_key != ?', self.object_key)
-
.order(:event_date, :created_at => :desc).first
-
-
# Return Strongly Typed Asset
-
AssetEvent.as_typed_event(event)
-
end
-
-
# Get the chronologically preceding event on this event's asset of the same type as the caller
-
# If one already exists for the same event_date, return the last created
-
# If none exists, returns nil
-
1
def previous_event_of_type
-
event = Rails.application.config.asset_base_class_name.constantize.get_typed_asset(self.send(Rails.application.config.asset_base_class_name.underscore)).asset_events
-
.where("asset_event_type_id = ?", self.asset_event_type_id) # get events of same type
-
.where("event_date < ? OR (event_date = ? AND created_at < ?)", self.event_date, self.event_date, (self.new_record? ? Time.current : self.created_at) ) # Define a window that runs up to this event
-
.where('object_key != ?', self.object_key)
-
.order(:event_date, :created_at => :asc).last
-
-
# Return Strongly Typed Asset
-
AssetEvent.as_typed_event(event)
-
end
-
-
#------------------------------------------------------------------------------
-
#
-
# Protected Methods
-
#
-
#------------------------------------------------------------------------------
-
1
protected
-
-
# Set resonable defaults for a new asset event
-
1
def set_defaults
-
248
self.event_date ||= Date.today
-
end
-
-
1
def set_base_transam_asset
-
120
self.base_transam_asset = transam_asset.try(:transam_asset) || transam_asset
-
end
-
-
1
def validate_event_date_with_purchase
-
152
if event_date.nil?
-
errors.add(:event_date, "must exist")
-
152
elsif transam_asset.try(:purchased_new) && event_date < transam_asset.purchase_date
-
errors.add(:event_date, "must be on or after purchase date if new purchase")
-
end
-
end
-
end
-
# During a rehabilitation update, subsystems can be selected and associated
-
# with a cost
-
1
class AssetEventAssetSubsystem < ActiveRecord::Base
-
#-----------------------------------------------------------------------------
-
# Callbacks
-
#-----------------------------------------------------------------------------
-
1
after_initialize :set_defaults
-
-
#-----------------------------------------------------------------------------
-
# Associations
-
#-----------------------------------------------------------------------------
-
# Every sign_order_sign belongs to a sign
-
1
belongs_to :rehabilitation_update_event,
-
:class_name => 'RehabilitationUpdateEvent',
-
:foreign_key => "asset_event_id",
-
:inverse_of => :asset_event_asset_subsystems
-
-
# Every asset_event_subsystem belongs to a subsystem
-
1
belongs_to :asset_subsystem
-
-
#-----------------------------------------------------------------------------
-
# Scopes
-
#-----------------------------------------------------------------------------
-
-
#-----------------------------------------------------------------------------
-
# Validations
-
#-----------------------------------------------------------------------------
-
1
validates :asset_subsystem, :presence => true
-
1
validates :rehabilitation_update_event, :presence => true
-
1
validates :parts_cost, :numericality => {:only_integer => true, :greater_than_or_equal_to => 0}, allow_nil: true
-
1
validates :labor_cost, :numericality => {:only_integer => true, :greater_than_or_equal_to => 0}, allow_nil: true
-
-
#-----------------------------------------------------------------------------
-
# Constants
-
#-----------------------------------------------------------------------------
-
-
# List of allowable form param hash keys
-
1
FORM_PARAMS = [
-
:id,
-
:asset_subsystem_id,
-
:asset_event_id,
-
:parts_cost,
-
:labor_cost
-
]
-
-
#-----------------------------------------------------------------------------
-
#
-
# Class Methods
-
#
-
#-----------------------------------------------------------------------------
-
-
1
def self.allowable_params
-
3
FORM_PARAMS
-
end
-
-
#-----------------------------------------------------------------------------
-
#
-
# Instance Methods
-
#
-
#-----------------------------------------------------------------------------
-
1
def cost
-
1
parts_cost + labor_cost
-
end
-
#-----------------------------------------------------------------------------
-
#
-
# Protected Methods
-
#
-
#-----------------------------------------------------------------------------
-
1
protected
-
-
# Set resonable defaults for a new instance
-
1
def set_defaults
-
-
end
-
end
-
1
class AssetEventType < ActiveRecord::Base
-
-
# All order types that are available
-
1
scope :active, -> { where(:active => true) }
-
-
1
def to_s
-
1
name
-
end
-
-
end
-
#------------------------------------------------------------------------------
-
# AssetGroup
-
#
-
# HBTM relationship with assets. Used as a generic bucket for grouping assets
-
# into loosely defined collections
-
#
-
#------------------------------------------------------------------------------
-
1
class AssetGroup < ActiveRecord::Base
-
-
# Include the object key mixin
-
1
include TransamObjectKey
-
-
#------------------------------------------------------------------------------
-
# Callbacks
-
#------------------------------------------------------------------------------
-
-
# Clear the mapping table when the group is destroyed
-
3
before_destroy { assets.clear }
-
-
#------------------------------------------------------------------------------
-
# Associations
-
#------------------------------------------------------------------------------
-
-
# Every asset group is owned by an organization
-
1
belongs_to :organization
-
-
# Every asset grouop has zero or more assets
-
1
has_and_belongs_to_many :assets, :join_table => :asset_groups_assets, :class_name => Rails.application.config.asset_base_class_name
-
-
#------------------------------------------------------------------------------
-
# Scopes
-
#------------------------------------------------------------------------------
-
-
# All order types that are available
-
1
scope :active, -> { where(:active => true) }
-
-
#------------------------------------------------------------------------------
-
# Validations
-
#------------------------------------------------------------------------------
-
1
validates :organization, :presence => true
-
1
validates :name, :presence => true, :length => { maximum: 64 }, :uniqueness => {:scope => :organization, :case_sensitive => false, :message => "must be unique within an organization"}
-
1
validates :code, :presence => true, :length => { maximum: 8 }
-
1
validates :description, :presence => true
-
-
# List of hash parameters allowed by the controller
-
1
FORM_PARAMS = [
-
:object_key,
-
:organization_id,
-
:name,
-
:code,
-
:description,
-
:active
-
]
-
-
# List of fields which can be searched using a simple text-based search
-
1
SEARCHABLE_FIELDS = [
-
:object_key,
-
:name,
-
:code,
-
:description
-
]
-
-
#------------------------------------------------------------------------------
-
#
-
# Class Methods
-
#
-
#------------------------------------------------------------------------------
-
-
1
def self.allowable_params
-
3
FORM_PARAMS
-
end
-
-
#------------------------------------------------------------------------------
-
#
-
# Instance Methods
-
#
-
#------------------------------------------------------------------------------
-
-
# Returns true if the asset group contains a homogeneous set of asset types, false otherwise
-
1
def homogeneous?
-
2
asset_type_ids.length == 1
-
end
-
-
# Returns the unique set of asset_ids for assets stored in the group
-
1
def asset_type_ids
-
3
assets.scope.uniq.pluck(:asset_type_id)
-
end
-
-
1
def to_s
-
3
name
-
end
-
-
1
def searchable_fields
-
1
SEARCHABLE_FIELDS
-
end
-
-
end
-
#-------------------------------------------------------------------------------
-
#
-
# Asset Tag
-
#
-
# Map relation that maps an asset to a user as part of a tag.
-
#
-
#-------------------------------------------------------------------------------
-
1
class AssetGroupsAsset < ActiveRecord::Base
-
#-----------------------------------------------------------------------------
-
# Callbacks
-
#-----------------------------------------------------------------------------
-
-
#-----------------------------------------------------------------------------
-
# Associations
-
#-----------------------------------------------------------------------------
-
-
1
belongs_to :asset
-
1
belongs_to :transam_asset
-
-
1
belongs_to :asset_group
-
-
#-----------------------------------------------------------------------------
-
# Scopes
-
#-----------------------------------------------------------------------------
-
-
#-----------------------------------------------------------------------------
-
# Validations
-
#-----------------------------------------------------------------------------
-
-
-
#-----------------------------------------------------------------------------
-
# Constants
-
#-----------------------------------------------------------------------------
-
-
#-----------------------------------------------------------------------------
-
#
-
# Class Methods
-
#
-
#-----------------------------------------------------------------------------
-
-
#-----------------------------------------------------------------------------
-
#
-
# Instance Methods
-
#
-
#-----------------------------------------------------------------------------
-
-
#-----------------------------------------------------------------------------
-
#
-
# Protected Methods
-
#
-
#-----------------------------------------------------------------------------
-
1
protected
-
-
end
-
# A subsystem within an asset, more granular/smaller than Equipment
-
# e.g. transmission or engine on a vehicle or windows in a facility
-
1
class AssetSubsystem < ActiveRecord::Base
-
1
belongs_to :asset_type
-
1
has_many :asset_event_asset_subsystems
-
-
2
scope :active, -> { where(:active => true) }
-
2
scope :for_type, ->(type_id) { where(asset_type_id: [type_id, nil]) } # NOTE: can handle id or coerce down an AssetType itself
-
-
1
validates :name, :presence => true
-
-
1
def to_s
-
20
name
-
end
-
end
-
1
class AssetSubtype < ActiveRecord::Base
-
-
# Associations
-
1
belongs_to :asset_type
-
-
# Validations
-
1
validates :asset_type, presence: true
-
-
# All order types that are available
-
1
scope :active, -> { where(:active => true) }
-
-
1
def full_name
-
1
"#{name} - #{description}"
-
end
-
-
1
def to_s
-
111
name
-
end
-
-
end
-
#-------------------------------------------------------------------------------
-
#
-
# Asset Tag
-
#
-
# Map relation that maps an asset to a user as part of a tag.
-
#
-
#-------------------------------------------------------------------------------
-
1
class AssetTag < ActiveRecord::Base
-
#-----------------------------------------------------------------------------
-
# Callbacks
-
#-----------------------------------------------------------------------------
-
-
#-----------------------------------------------------------------------------
-
# Associations
-
#-----------------------------------------------------------------------------
-
# Every asset_tag belongs to an asset
-
1
belongs_to :asset
-
-
# Every asset_tag belongs to a user
-
1
belongs_to :user
-
-
#-----------------------------------------------------------------------------
-
# Scopes
-
#-----------------------------------------------------------------------------
-
-
#-----------------------------------------------------------------------------
-
# Validations
-
#-----------------------------------------------------------------------------
-
1
validates :asset, :presence => true
-
1
validates :user, :presence => true
-
-
#-----------------------------------------------------------------------------
-
# Constants
-
#-----------------------------------------------------------------------------
-
-
# List of allowable form param hash keys
-
1
FORM_PARAMS = [
-
]
-
-
#-----------------------------------------------------------------------------
-
#
-
# Class Methods
-
#
-
#-----------------------------------------------------------------------------
-
-
1
def self.allowable_params
-
1
FORM_PARAMS
-
end
-
-
#-----------------------------------------------------------------------------
-
#
-
# Instance Methods
-
#
-
#-----------------------------------------------------------------------------
-
-
#-----------------------------------------------------------------------------
-
#
-
# Protected Methods
-
#
-
#-----------------------------------------------------------------------------
-
1
protected
-
-
end
-
1
class AssetType < ActiveRecord::Base
-
-
1
after_initialize :set_defaults
-
-
# associations
-
-
# every asset type has 0 or more sub types
-
1
has_many :asset_subtypes
-
-
# All order types that are available
-
2
scope :active, -> { where(:active => true) }
-
-
1
def full_name
-
1
"#{name} - #{description}"
-
end
-
-
1
def to_s
-
107
name
-
end
-
-
1
protected
-
-
1
def set_defaults
-
1569
self.allow_parent = self.allow_parent.nil? ? true : self.allow_parent
-
end
-
-
end
-
-
class AssetUpdaterProxy < Proxy
-
# General state variables
-
-
# Type of asset type to process
-
attr_accessor :asset_types, :asset_groups, :policy
-
-
# Basic validations. Just checking that the form is complete
-
validates :asset_types, :presence => true
-
-
def initialize(attrs = {})
-
super
-
attrs.each do |k, v|
-
self.send "#{k}=", v
-
end
-
self.asset_types ||= []
-
end
-
end
-
#-------------------------------------------------------------------------------
-
#
-
# Comment
-
#
-
# A comment that has been associated with another class such as a Task etc. This is a
-
# polymorphic class that can store comments against any class that includes a
-
# commentable association
-
#
-
# To use this class as an association with another class include the following line into
-
# the model
-
#
-
# has_many :comments, :as => :commentable
-
#
-
#-------------------------------------------------------------------------------
-
1
class Comment < ActiveRecord::Base
-
-
# Include the object key mixin
-
1
include TransamObjectKey
-
-
# Callbacks
-
1
after_initialize :set_defaults
-
-
# Associations
-
1
belongs_to :commentable, :polymorphic => true
-
-
# Each comment was created by a user
-
1
belongs_to :creator, -> { unscope(where: :active) }, :class_name => "User", :foreign_key => "created_by_id"
-
-
1
validates :comment, :presence => true
-
1
validates :created_by_id, :presence => true
-
-
97
default_scope { order('created_at DESC') }
-
-
# List of hash parameters allowed by the controller
-
1
FORM_PARAMS = [
-
:comment,
-
:commentable_id,
-
:commentable_type,
-
:created_by_id
-
]
-
-
# List of fields which can be searched using a simple text-based search
-
1
SEARCHABLE_FIELDS = [
-
:object_key,
-
:comment
-
]
-
-
#-----------------------------------------------------------------------------
-
#
-
# Class Methods
-
#
-
#-----------------------------------------------------------------------------
-
-
1
def self.allowable_params
-
3
FORM_PARAMS
-
end
-
-
#-----------------------------------------------------------------------------
-
#
-
# Instance Methods
-
#
-
#-----------------------------------------------------------------------------
-
-
1
def searchable_fields
-
1
SEARCHABLE_FIELDS
-
end
-
-
1
def to_s
-
1
comment
-
end
-
-
1
def description
-
1
comment
-
end
-
-
# Return the organization of the owning object so instances can be index using
-
# the keyword indexer
-
1
def organization
-
2
if commentable.respond_to? :organization
-
1
commentable.organization
-
else
-
1
creator.organization
-
end
-
end
-
-
#-----------------------------------------------------------------------------
-
#
-
# Protected Methods
-
#
-
#-----------------------------------------------------------------------------
-
1
protected
-
-
# Set resonable defaults for a new asset event
-
1
def set_defaults
-
-
end
-
-
#-----------------------------------------------------------------------------
-
#
-
# Private Methods
-
#
-
#-----------------------------------------------------------------------------
-
1
private
-
-
end
-
#-------------------------------------------------------------------------------
-
#
-
# FiscalYear
-
#
-
# Mixin that adds fiscal year methods to a class
-
#
-
#-------------------------------------------------------------------------------
-
1
module FiscalYear
-
1
include FiscalYearHelper
-
-
# Returns the date of the start of a fiscal year for a given calendar year. For
-
# a year like 2015 we return the start of the FY-15-16 year
-
1
def start_of_fiscal_year date_year
-
# System Config provides a string giving the start day of the fiscal year as "mm-dd" eg 07-01 for July 1st. We can
-
# append the date year to this and generate the date of the fiscal year starting in the date calendar year
-
208
date_str = "#{SystemConfig.instance.start_of_fiscal_year}-#{date_year}"
-
-
208
start_of_fiscal_year = Date.strptime(date_str, "%m-%d-%Y")
-
end
-
-
1
def end_of_fiscal_year date_year
-
(start_of_fiscal_year(date_year) + 1.year) - 1.day
-
end
-
-
# Returns the first day of the planning year which is the next fiscal year
-
1
def start_of_planning_year
-
1
start_of_fiscal_year current_planning_year_year
-
end
-
# returns the fiscal year epoch -- the first allowable fiscal year for the application
-
1
def fiscal_year_epoch_year
-
2
fiscal_year_year_on_date(SystemConfig.instance.epoch)
-
end
-
-
1
def fiscal_year_epoch
-
1
fiscal_year(fiscal_year_epoch_year)
-
end
-
#
-
# Returns the current fiscal year as a calendar year (integer). Each fiscal year is represented as the year in which
-
# the fiscal year started, so FY 13-14 would return 2013 as a numeric
-
#
-
1
def current_fiscal_year_year(use_system_config=true)
-
-
137
fy_year = SystemConfig.instance.fy_year if use_system_config
-
137
fy_year = fiscal_year_year_on_date(Date.today) if fy_year.blank? || fiscal_year_year_on_date(Date.today) - fy_year > 1 # dont let a manual rollover go over 2 years
-
-
137
fy_year
-
-
end
-
#
-
# Returns the current planning year which is always the next fiscal year
-
#
-
1
def current_planning_year_year
-
106
current_fiscal_year_year + 1
-
end
-
-
# Returns the last fiscal year in the planning horizon
-
1
def last_fiscal_year_year
-
24
current_fiscal_year_year + SystemConfig.instance.num_forecasting_years
-
end
-
-
# Returns the fiscal year on a given date
-
1
def fiscal_year_year_on_date(date)
-
-
198
if date.nil?
-
date = Date.today
-
end
-
198
date_year = date.year
-
-
# If the start of the fiscal year in the calendar year is before date, we are in the fiscal year that starts in this
-
# calendar years, otherwise the date is in the fiscal year that started the previous calendar year
-
198
(date < start_of_fiscal_year(date_year)) ? date_year - 1 : date_year
-
-
end
-
-
# Returns the end date of the fiscal year of a given date
-
1
def fiscal_year_end_date(date=Date.today)
-
2
date_year = fiscal_year_year_on_date(date)
-
-
# System Config provides a string giving the start day of the fiscal year as "mm-dd" eg 07-01 for July 1st. We can
-
# append the date year to this and generate the date of the fiscal year starting in the date calendar year
-
2
date_str = "#{SystemConfig.instance.start_of_fiscal_year}-#{date_year}"
-
-
2
start_of_fiscal_year = Date.strptime(date_str, "%m-%d-%Y")
-
-
# get end of last fiscal year and add a year for current fiscal year
-
2
start_of_fiscal_year - 1.days + 1.years
-
-
end
-
-
# Returns the current fiscal year as a formatted FY string
-
1
def current_fiscal_year
-
1
fiscal_year(current_fiscal_year_year)
-
end
-
-
# Returns the current planning year (the FY after the current one)
-
# as a formatted FY string
-
1
def current_planning_year
-
1
fiscal_year(current_planning_year_year)
-
end
-
-
# Returns the fiscal year for a date as a formatted FY string
-
1
def fiscal_year_on_date(date)
-
3
fiscal_year(fiscal_year_year_on_date(date))
-
end
-
-
# Returns the calendar year formatted as a FY string
-
1
def fiscal_year(year, klass = nil)
-
-
# some controllers might have a special formatter instead of the default one to use the FY string
-
# eventually default might be a SystemConfig.instance attribute as well but for now hard-coded
-
-
205
unless klass
-
205
if defined? params
-
52
klass = params[:controller].classify
-
153
elsif self.class.to_s.include? 'Controller'
-
klass = self.class.to_s[0..-('Controller'.length+1)]
-
else
-
153
klass = self.class.to_s
-
end
-
end
-
-
205
formatter = SystemConfig.instance.special_fiscal_year_formatters[klass]
-
205
formatter = SystemConfig.instance.default_fiscal_year_formatter if formatter.nil?
-
-
205
if formatter == 'start_year'
-
"#{year}"
-
205
elsif formatter == 'end_year'
-
"#{year+1}"
-
else
-
205
yr = year - fy_century(year)
-
205
first = "%.2d" % yr
-
205
if yr == 99 # when yr == 99, yr + 1 would be 100, which causes: "FY 99-100"
-
next_yr = 00
-
else
-
205
next_yr = (yr + 1)
-
end
-
205
last = "%.2d" % next_yr
-
205
"FY #{first}-#{last}"
-
end
-
end
-
-
# Returns a select array of fiscal years that includes fiscal years that
-
# are before the current fiscal year
-
1
def get_all_fiscal_years
-
2
get_fiscal_years(Date.today - 4.years)
-
end
-
-
# Returns a select array of fiscal years
-
1
def get_fiscal_years(date = nil, num_forecasting_years = nil)
-
9
if date
-
7
current_year = fiscal_year_year_on_date(date)
-
else
-
2
current_year = current_planning_year_year
-
end
-
9
a = []
-
-
# get last fiscal year from current year and num of forecasting years
-
9
if num_forecasting_years.nil?
-
6
last_year = last_fiscal_year_year
-
else
-
3
last_year = current_year + num_forecasting_years
-
end
-
-
9
(current_year..last_year).each do |year|
-
121
a << [fiscal_year(year), year]
-
end
-
9
a
-
end
-
-
1
def get_past_fiscal_years
-
date = Date.today-(SystemConfig.instance.num_forecasting_years).years
-
num_forecasting_years = SystemConfig.instance.num_forecasting_years-1
-
-
get_fiscal_years(date,num_forecasting_years)
-
end
-
-
# Returns a select array of fiscal years remaining in the planning period
-
1
def get_planning_fiscal_years(date = Date.today)
-
4
current_year = fiscal_year_year_on_date(date)
-
4
a = []
-
4
(current_year..last_fiscal_year_year).each do |year|
-
64
a << [fiscal_year(year), year]
-
end
-
4
a
-
end
-
-
# Determines the century for the year. Assumes assets are no older than 1900
-
1
def fy_century(fy)
-
207
fy < 2000 ? 1900 : 2000
-
end
-
-
end
-
module MaintainableAsset
-
#-----------------------------------------------------------------------------
-
#
-
# MaintainableAsset
-
#
-
# Mixin that adds maintenance updates to an Asset
-
#
-
#-----------------------------------------------------------------------------
-
extend ActiveSupport::Concern
-
-
included do
-
-
#---------------------------------------------------------------------------
-
# Associations
-
#---------------------------------------------------------------------------
-
-
# each asset has zero or more condition updates
-
has_many :maintenance_updates, -> {where :asset_event_type_id => MaintenanceUpdateEvent.asset_event_type.id }, :as => Rails.application.config.asset_base_class_name.underscore.to_sym, :class_name => "MaintenanceUpdateEvent"
-
#---------------------------------------------------------------------------
-
# Validations
-
#---------------------------------------------------------------------------
-
-
end
-
-
#-----------------------------------------------------------------------------
-
# Class Methods
-
#-----------------------------------------------------------------------------
-
-
#-----------------------------------------------------------------------------
-
# Instance Methods
-
#-----------------------------------------------------------------------------
-
-
# Forces an update of an assets reported condition. This performs an update on the record.
-
def update_maintenance
-
-
Rails.logger.debug "Updating recorded maintenance for asset = #{object_key}"
-
-
# can't do this if it is a new record as none of the IDs would be set
-
unless new_record? or disposed?
-
if maintenance_updates.empty?
-
self.last_maintenance_date = nil
-
else
-
event = maintenance_updates.last
-
self.last_maintenance_date = event.event_date
-
-
if self.respond_to? :mileage_updates
-
# See if there was a mileage update as well but only if this date is later
-
# than the last reported mileage date
-
if event.current_mileage.to_i > 0 and event.event_date > self.reported_mileage_date
-
self.reported_mileage = event.current_mileage.to_i
-
self.reported_mileage_date = event.event_date
-
end
-
end
-
end
-
# save changes to this asset
-
save(:validate => false)
-
end
-
-
end
-
#-----------------------------------------------------------------------------
-
-
end
-
1
module PolicyAware
-
-
1
extend ActiveSupport::Concern
-
-
1
included do
-
-
-
# Before the asset is updated we may need to update things like estimated
-
# replacement cost if they updated other things
-
# updates the calculated values of an asset
-
1
after_save :check_policy_rule
-
1
after_save :update_asset_state
-
-
end
-
-
1
module ClassMethods
-
1
def self.cleansable_fields
-
[
-
'policy_replacement_year',
-
'scheduled_replacement_year',
-
'scheduled_replacement_cost',
-
'in_backlog'
-
]
-
end
-
end
-
-
1
def policy
-
16
organization.get_policy
-
end
-
-
#-----------------------------------------------------------------------------
-
# Return the policy analyzer for this asset. If a policy is not provided the
-
# default policy for the asset is used
-
#-----------------------------------------------------------------------------
-
1
def policy_analyzer(policy_to_use=nil)
-
10
if policy_to_use.blank?
-
10
policy_to_use = policy
-
end
-
10
policy_analyzer = Rails.application.config.policy_analyzer.constantize.new(TransamAsset.get_typed_asset(self), policy_to_use)
-
end
-
-
1
def estimated_condition_rating
-
# Estimate the year that the asset will need replacing amd the estimated
-
# condition of the asset
-
begin
-
class_name = policy_analyzer.get_condition_estimation_type.class_name
-
calculate(TransamAsset.get_typed_asset(self), class_name)
-
rescue Exception => e
-
Rails.logger.warn e.message
-
Rails.logger.error e.backtrace
-
end
-
end
-
1
def estimated_condition_type
-
ConditionType.from_rating(estimated_condition_rating)
-
end
-
-
1
protected
-
-
# updates the calculated values of an asset
-
1
def update_asset_state
-
-
3
return unless self.replacement_by_policy? || self.replacement_pinned?
-
-
3
Rails.logger.debug "Updating SOGR for transam asset = #{object_key}"
-
-
3
if disposed?
-
Rails.logger.debug "Asset #{object_key} is disposed"
-
end
-
-
# returns the year in which the asset should be replaced based on the policy and asset
-
# characteristics
-
3
begin
-
# store old policy replacement year for use later
-
3
old_policy_replacement_year = policy_replacement_year
-
-
# see what metric we are using to determine the service life of the asset
-
3
class_name = policy_analyzer.get_service_life_calculation_type.class_name
-
-
1
new_policy_replacement_year = calculate(TransamAsset.get_typed_asset(self), class_name)
-
1
update_columns(policy_replacement_year: new_policy_replacement_year) if old_policy_replacement_year != new_policy_replacement_year
-
-
1
if self.scheduled_replacement_year.nil? or self.scheduled_replacement_year == old_policy_replacement_year
-
1
Rails.logger.debug "Setting scheduled replacement year to #{policy_replacement_year}"
-
1
update_columns(scheduled_replacement_year: self.policy_replacement_year, in_backlog: false)
-
end
-
# If the asset is in backlog set the scheduled year to the current FY year
-
1
if self.scheduled_replacement_year < current_planning_year_year
-
1
Rails.logger.debug "Asset is in backlog. Setting scheduled replacement year to #{current_planning_year_year}"
-
1
update_columns(scheduled_replacement_year: current_planning_year_year, in_backlog: true)
-
end
-
rescue Exception => e
-
2
Rails.logger.warn e.message
-
2
Rails.logger.warn e.backtrace
-
end
-
-
# If the policy replacement year changes we need to check to see if the asset
-
# is in backlog and update the scheduled replacement year to the first planning
-
# year
-
3
if self.policy_replacement_year < current_planning_year_year
-
3
update_columns(in_backlog: true)
-
else
-
update_columns(in_backlog: false) if self.in_backlog != false
-
end
-
-
3
Rails.logger.debug "New scheduled_replacement_year = #{self.scheduled_replacement_year}"
-
# Get the calculator class from the policy analyzer
-
3
class_name = policy_analyzer.get_replacement_cost_calculation_type.class_name
-
3
calculator_instance = class_name.constantize.new
-
3
start_date = start_of_fiscal_year(scheduled_replacement_year) unless scheduled_replacement_year.blank?
-
3
Rails.logger.debug "Start Date = #{start_date}"
-
3
get_sched_cost = (calculator_instance.calculate_on_date(self, start_date)+0.5).to_i
-
3
update_columns(scheduled_replacement_cost: get_sched_cost) if get_sched_cost != self.scheduled_replacement_cost
-
-
#self.early_replacement_reason = nil if check_early_replacement && !is_early_replacement?
-
end
-
-
1
def check_policy_rule
-
-
3
policy.find_or_create_asset_type_rule self.asset_subtype.asset_type
-
-
3
typed_asset = TransamAsset.get_typed_asset self
-
3
if (typed_asset.respond_to? :fuel_type)
-
policy.find_or_create_asset_subtype_rule asset_subtype, typed_asset.fuel_type
-
else
-
3
policy.find_or_create_asset_subtype_rule asset_subtype
-
end
-
end
-
-
1
private
-
-
# Calls a calculate method on a Calculator class to perform a condition or cost calculation
-
# for the asset. The method name defaults to x.calculate(asset) but other methods
-
# with the same signature can be passed in
-
2
def calculate(asset, class_name, target_method = 'calculate')
-
begin
-
1
Rails.logger.debug "#{class_name}, #{target_method}"
-
# create an instance of this class and call the method
-
1
calculator_instance = class_name.constantize.new
-
1
Rails.logger.debug "Instance created #{calculator_instance}"
-
1
method_object = calculator_instance.method(target_method)
-
1
Rails.logger.debug "Instance method created #{method_object}"
-
1
method_object.call(asset)
-
rescue Exception => e
-
Rails.logger.error e.message
-
Rails.logger.error e.backtrace
-
raise RuntimeError.new "#{class_name} calculation failed for asset #{asset.object_key} and policy #{policy.name}"
-
end
-
end
-
-
end
-
#
-
#
-
# mixin for dispositions, early disposition requests, policy replacement, rehab, and expected useful life
-
#
-
#
-
#
-
-
-
1
module ReplaceableAsset
-
-
1
extend ActiveSupport::Concern
-
-
1
included do
-
-
#---------------------------------------------------------------------------
-
# Associations
-
#---------------------------------------------------------------------------
-
-
# each asset has zero or more scheduled replacement updates
-
1
has_many :schedule_replacement_updates, -> {where :asset_event_type_id => ScheduleReplacementUpdateEvent.asset_event_type.id }, :class_name => "ScheduleReplacementUpdateEvent", :as => :transam_asset
-
-
# each asset has zero or more scheduled rehabilitation updates
-
1
has_many :schedule_rehabilitation_updates, -> {where :asset_event_type_id => ScheduleRehabilitationUpdateEvent.asset_event_type.id }, :class_name => "ScheduleRehabilitationUpdateEvent", :as => :transam_asset
-
-
# each asset has zero or more recorded rehabilitation events
-
3
has_many :rehabilitation_updates, -> {where :asset_event_type_id => RehabilitationUpdateEvent.asset_event_type.id}, :class_name => "RehabilitationUpdateEvent", :as => :transam_asset
-
1
accepts_nested_attributes_for :rehabilitation_updates, :reject_if => Proc.new{|ae| ae['total_cost'].blank? }, :allow_destroy => true
-
-
# each asset has zero or more scheduled disposition updates
-
1
has_many :schedule_disposition_updates, -> {where :asset_event_type_id => ScheduleDispositionUpdateEvent.asset_event_type.id }, :class_name => "ScheduleDispositionUpdateEvent", :as => :transam_asset
-
-
# each asset has zero or more disposition updates
-
1
has_many :disposition_updates, -> {where :asset_event_type_id => DispositionUpdateEvent.asset_event_type.id }, :class_name => "DispositionUpdateEvent", :as => :transam_asset
-
-
# each asset has zero or more early disposition requests
-
1
has_many :early_disposition_requests, -> {where :asset_event_type_id => EarlyDispositionRequestUpdateEvent.asset_event_type.id }, :class_name => "EarlyDispositionRequestUpdateEvent", :as => :transam_asset
-
-
#---------------------------------------------------------------------------
-
# Validations
-
#---------------------------------------------------------------------------
-
-
#---------------------------------------------------------------------------
-
# Scopes
-
#---------------------------------------------------------------------------
-
-
# Returns a list of assets that have been disposed
-
1
scope :disposed, -> { where.not(disposition_date: nil) }
-
-
1
scope :in_transfer, -> { where(TransamAsset.arel_table[:asset_tag].eq(TransamAsset.arel_table[:object_key])) }
-
171
scope :not_in_transfer, -> { where(TransamAsset.arel_table[:asset_tag].not_eq(TransamAsset.arel_table[:object_key])) }
-
-
# Returns a list of assets that are still operational
-
170
scope :operational, -> { not_in_transfer.where(disposition_date: nil) }
-
-
1
scope :operational_in_range, -> (start_date, end_date) { where(TransamAsset.arel_table[:in_service_date].not_eq(nil).and(TransamAsset.arel_table[:in_service_date].lteq(end_date)).and(TransamAsset.arel_table[:disposition_date].eq(nil).or(TransamAsset.arel_table[:disposition_date].gt(end_date)))) }
-
-
# Returns a list of asset that in early replacement
-
1
scope :early_replacement, -> { where('policy_replacement_year is not NULL and scheduled_replacement_year is not NULL and scheduled_replacement_year < policy_replacement_year') }
-
-
end
-
-
1
module ClassMethods
-
1
def self.allowable_params
-
[
-
{rehabilitation_updates_attributes: RehabilitationUpdateEvent.allowable_params}
-
]
-
end
-
-
1
def self.cleansable_fields
-
[
-
'disposition_date',
-
'early_replacement_reason',
-
'scheduled_rehabilitation_year',
-
'scheduled_disposition_year'
-
]
-
end
-
end
-
-
-
-
-
1
def disposed?
-
5
disposition_date.present?
-
end
-
-
# Returns true if the asset can be disposed in the next planning cycle,
-
# false otherwise
-
1
def disposable?( include_early_disposal_request_approved_via_transfer = false)
-
return false if disposed?
-
# otherwise check the policy year and see if it is less than or equal to
-
# the current planning year
-
return false if policy_replacement_year.blank?
-
-
if policy_replacement_year <= current_planning_year_year
-
# After ESL disposal
-
true
-
else
-
# Prior ESL disposal request
-
last_request = early_disposition_requests.last
-
if include_early_disposal_request_approved_via_transfer
-
last_request.try(:is_approved?)
-
else
-
last_request.try(:is_unconditional_approved?)
-
end
-
end
-
end
-
-
# Returns true if the asset can be requested for early disposal
-
1
def eligible_for_early_disposition_request?
-
return false if disposed?
-
# otherwise check the policy year and see if it is less than or equal to
-
# the current planning year
-
return false if policy_replacement_year.blank?
-
-
if policy_replacement_year <= current_planning_year_year
-
# Eligible for after ESL disposal
-
false
-
else
-
# Prior ESL disposal request
-
last_request = early_disposition_requests.last
-
# No previous request or was rejected
-
!last_request || last_request.try(:is_rejected?)
-
end
-
end
-
-
1
def expected_useful_life
-
1
purchased_new ? policy_analyzer.get_min_service_life_months : policy_analyzer.get_min_used_purchase_service_life_months
-
end
-
-
1
def policy_rehabilitation_year
-
# Check for rehabilitation policy events
-
begin
-
# Use the calculator to calculate the policy rehabilitation fiscal year
-
calculator = RehabilitationYearCalculator.new
-
return calculator.calculate(TransamAsset.get_typed_asset(self))
-
rescue Exception => e
-
Rails.logger.warn e.message
-
Rails.logger.error e.backtrace
-
end
-
end
-
-
1
def estimated_replacement_year
-
# Estimate the year that the asset will need replacing
-
begin
-
class_name = policy_analyzer.get_condition_estimation_type.class_name
-
calculate(TransamAsset.get_typed_asset(self), class_name, 'last_servicable_year') + 1
-
rescue Exception => e
-
Rails.logger.warn e.message
-
Rails.logger.error e.backtrace
-
end
-
end
-
-
1
def estimated_replacement_cost
-
if self.policy_replacement_year < current_planning_year_year
-
start_date = start_of_fiscal_year(scheduled_replacement_year)
-
else
-
start_date = start_of_fiscal_year(policy_replacement_year)
-
end
-
# Update the estimated replacement costs
-
class_name = policy_analyzer.get_replacement_cost_calculation_type.class_name
-
calculator_instance = class_name.constantize.new
-
(calculator_instance.calculate_on_date(self, start_date)+0.5).to_i
-
end
-
-
# Returns true if an asset is scheduled for disposition
-
1
def scheduled_for_disposition?
-
(scheduled_disposition_year.present? and disposed? == false)
-
end
-
-
1
def is_early_replacement?
-
policy_replacement_year && scheduled_replacement_year && scheduled_replacement_year < policy_replacement_year
-
end
-
-
1
def update_early_replacement_reason(reason = nil)
-
if is_early_replacement?
-
self.early_replacement_reason = reason
-
else
-
self.early_replacement_reason = nil
-
end
-
end
-
-
1
def formatted_early_replacement_reason
-
if early_replacement_reason.present?
-
early_replacement_reason
-
else
-
'(Reason not provided)'
-
end
-
end
-
-
1
def last_rehabilitation_date
-
1
rehabilitation_updates.last.try(:event_date)
-
end
-
-
-
end
-
#------------------------------------------------------------------------------
-
#
-
# TransamAttributes
-
#
-
# Mixin that creates a list of allowable model attributes for the Transam Controllers
-
# This class should be overridden to add updated attribute lists based on derived
-
# sub classes
-
#
-
#------------------------------------------------------------------------------
-
1
module TransamAttributes
-
-
1
def asset_allowable_params
-
Asset.allowable_params
-
end
-
-
1
def asset_event_allowable_params
-
AssetEvent.allowable_params +
-
ConditionUpdateEvent.allowable_params +
-
ServiceStatusUpdateEvent.allowable_params +
-
2
ScheduleRehabilitationUpdateEvent.allowable_params +
-
DispositionUpdateEvent.allowable_params
-
end
-
-
1
def attachment_allowable_params
-
Attachment.allowable_params
-
end
-
-
1
def message_allowable_params
-
Message.allowable_params
-
end
-
-
1
def notice_allowable_params
-
Notice.allowable_params
-
end
-
-
1
def organization_allowable_params
-
2
Organization.allowable_params #+ TransitAgency.allowable_params
-
end
-
-
1
def policy_allowable_params
-
Policy.allowable_params
-
end
-
-
1
def task_allowable_params
-
Task.allowable_params
-
end
-
-
1
def upload_allowable_params
-
Upload.allowable_params
-
end
-
-
1
def user_allowable_params
-
7
User.allowable_params
-
end
-
-
1
def location_allowable_params
-
Location.allowable_params
-
end
-
-
end
-
#------------------------------------------------------------------------------
-
#
-
# TransamGuid
-
#
-
# Adds a unique GUID to a model
-
#
-
#------------------------------------------------------------------------------
-
module TransamGuid
-
-
extend ActiveSupport::Concern
-
-
included do
-
# Always generate a unique GUID before saving to the database
-
before_validation(:on => :create) do
-
generate_guid(:guid)
-
end
-
-
validates :guid, :presence => true, :uniqueness => true
-
end
-
-
#-----------------------------------------------------------------------------
-
#
-
# Class Methods
-
#
-
#-----------------------------------------------------------------------------
-
-
module ClassMethods
-
-
end
-
-
# Only generate an GUID if one is not set. This allows objects to be
-
# replaced but maintain the same GUID
-
def generate_guid(column)
-
if self[column].blank?
-
begin
-
self[column] = SecureRandom.uuid
-
end
-
end
-
end
-
-
protected
-
-
end
-
#------------------------------------------------------------------------------
-
#
-
# NumericSanitizers
-
#
-
# Mixin that adds numeric sanitizers to a model
-
#
-
#------------------------------------------------------------------------------
-
1
module TransamNumericSanitizers
-
-
1
extend ActiveSupport::Concern
-
-
1
included do
-
-
end
-
-
#------------------------------------------------------------------------------
-
#
-
# Class Methods
-
#
-
#------------------------------------------------------------------------------
-
1
module ClassMethods
-
-
end
-
#------------------------------------------------------------------------------
-
#
-
# Instance Methods
-
#
-
#------------------------------------------------------------------------------
-
-
-
# Strip extraneous non-numeric characters from an input number and return a float
-
1
def sanitize_to_float(num)
-
2428
num.to_s.gsub('$%\'"','').scan(/\b-?[\d.]+/).join.to_f
-
end
-
-
# Strip extraneous non-numeric characters from an input number and return a float
-
1
def sanitize_to_int(num)
-
1972
sanitize_to_float(num).to_i
-
end
-
-
end
-
#------------------------------------------------------------------------------
-
#
-
# TransamObjectKey
-
#
-
# Adds a unique object key to a model
-
#
-
#------------------------------------------------------------------------------
-
1
module TransamObjectKey
-
-
1
extend ActiveSupport::Concern
-
-
1
included do
-
# Always generate a unique object key before saving to the database
-
24
before_validation(:on => :create) do
-
1808
generate_object_key(:object_key)
-
end
-
-
24
validates :object_key, :presence => true, :length => { is: 12 }
-
24
validates :object_key, :uniqueness => true, unless: :skip_uniqueness?
-
end
-
-
# Only generate an objecrt key if one is not set. This allows objects to be
-
# replaced but maintain the same object key
-
1
def generate_object_key(column)
-
1808
if self[column].blank?
-
begin
-
# Note Time.now.to_f converts to seconds since Epoch, which is correct
-
1807
self[column] = (Time.now.to_f * 10000000).to_i.to_s(24).upcase
-
end #while self.exists?(column => self[column])
-
end
-
end
-
# Use the object key as the restful parameter
-
1
def to_param
-
308
object_key
-
end
-
-
1
protected
-
-
1
def skip_uniqueness?
-
2824
false
-
end
-
end
-
#------------------------------------------------------------------------------
-
#
-
# TransamReadOnlyModel
-
#
-
# Mixin that forces read-only-ness to a model. Use this where the app is reading
-
# from a database view
-
#
-
#------------------------------------------------------------------------------
-
module TransamReadOnlyModel
-
-
extend ActiveSupport::Concern
-
-
included do
-
attr_readonly(*column_names) # Required to block update_attribute and update_column
-
end
-
-
def readonly?
-
true # Does not block destroy or delete
-
end
-
-
def destroy
-
raise ActiveRecord::ReadOnlyRecord
-
end
-
-
def delete
-
raise ActiveRecord::ReadOnlyRecord
-
end
-
-
end
-
1
module TransamTokenAuthentication
-
-
1
def reset_authentication_token
-
1
update_attributes(authentication_token: nil)
-
1
ensure_authentication_token
-
1
authentication_token.present?
-
end
-
-
# expose private last_attempt? method via this alias
-
# returns true if this is the last chance to enter password before locking account
-
1
def on_last_attempt?
-
1
last_attempt?
-
end
-
-
# Returns number of minutes before account will be unlocked
-
1
def time_until_unlock
-
return 0 unless access_locked?
-
return ((User.unlock_in - (Time.current - locked_at)) / 60).round
-
end
-
-
# Checks whether or not an API user is valid for authentication.
-
1
def valid_for_api_authentication?(password=nil)
-
# the valid_for_authentication? method is defined in Devise's models/authenticatable.rb and overloaded in models/lockable.rb
-
# passed block will only run if user is NOT locked out
-
2
valid_for_authentication? do
-
# check if password is correct
-
2
valid_password?(password)
-
end
-
end
-
-
end
-
1
module TransamWorkflow
-
1
extend ActiveSupport::Concern
-
-
1
included do
-
# Has 0 or more workflow events. Using a polymorphioc association.
-
3
has_many :workflow_events, :as => :accountable, :dependent => :destroy
-
-
3
validates :state, :presence => true
-
-
end
-
#------------------------------------------------------------------------------
-
#
-
# Class Methods
-
#
-
#------------------------------------------------------------------------------
-
-
# Returns a collection of classes that implement TransamWorkflow
-
1
def self.implementors
-
ObjectSpace.each_object(Class).select { |klass| klass < TransamWorkflow }
-
end
-
-
-
1
module ClassMethods
-
-
1
def transam_workflow_transitions
-
68
[]
-
# array of hashes in this format
-
# {event_name: event_name, from_state: from, to_state: to, guard: method_name, can: method_name, icon: font-awesome-icon-name, label: title, before: method_name, after: method_name}
-
end
-
-
# Return the list of allowable event names for this class
-
1
def event_names()
-
9
a = []
-
-
9
(transam_workflow_transitions.empty? ? state_machine : self.new.machine.definition).events.each do |e|
-
28
a << e.name.to_s
-
end
-
9
a
-
end
-
# Returns the list of allowable states for this class
-
1
def state_names()
-
2
a = []
-
2
(transam_workflow_transitions.empty? ? state_machine : self.new.machine.definition).states.each do |s|
-
11
a << s.name.to_s
-
end
-
2
a
-
end
-
-
1
def event_transitions(event=nil)
-
2
a = []
-
-
2
evts = (transam_workflow_transitions.empty? ? state_machine : self.new.machine.definition).events
-
2
evts = evts.select{|x| x.name.to_s == event.to_s} unless event.nil?
-
2
evts.each do |evt|
-
4
evt.branches.each do |branch|
-
8
branch.state_requirements.each do |state_req|
-
8
state_req[:from].values.each do |from|
-
28
a << {from => state_req[:to].values[0]}
-
end
-
end
-
end
-
end
-
-
2
a.uniq
-
end
-
-
# Returns the list of states that can transition to a target state
-
1
def state_predecessors state
-
1
a = []
-
1
event_transitions.each do |transition|
-
7
a << transition.keys.first if transition.values.first.to_s == state.to_s
-
end
-
-
1
a.uniq
-
end
-
-
# Returns the list of states that can precurse an event
-
1
def event_predecessors event
-
1
a = []
-
-
3
evt = (transam_workflow_transitions.empty? ? state_machine : self.new.machine.definition).events.find{|x|x.name == event.to_s}
-
-
1
evt.branches.each do |branch|
-
4
a << branch.state_requirements.map{ |state_req| state_req[:from].values}
-
end
-
-
1
a.flatten.uniq
-
end
-
-
end
-
-
#------------------------------------------------------------------------------
-
#
-
# Instance Methods
-
#
-
#------------------------------------------------------------------------------
-
-
# Get the allowable events for this state as strings (for use in views)
-
1
def allowable_events()
-
25
(self.class.transam_workflow_transitions.empty? ? self : self.machine).state_events.map{|x| x.to_s}
-
end
-
-
1
def allowable_event_objects
-
machine_obj = (self.class.transam_workflow_transitions.empty? ? self : self.machine)
-
machine_obj.class.state_machine.events.select{|x| machine_obj.state_events.include? x.name}
-
end
-
-
# Simple override of the workflow events. Always use this method as it can be
-
# filtered to limit viewable events
-
1
def history()
-
4
workflow_events
-
end
-
-
# Returns the correct icon for a workflow event
-
1
def get_workflow_event_icon(event_name)
-
-
custom_icon = self.class.transam_workflow_transitions.detect{|t| t[:event_name].to_s == event_name.to_s}.try(:[], :icon)
-
if custom_icon
-
custom_icon
-
elsif event_name == 'retract'
-
'fa-eject'
-
elsif event_name == 'transmit' || event_name == 'submit'
-
'fa-share'
-
elsif event_name == 'accept' || event_name == 'authorize'
-
'fa-check-square-o'
-
elsif event_name == 'start' || event_name == 'publish'
-
'fa-play'
-
elsif event_name == 'complete' || event_name == 'close'
-
'fa-check-square'
-
elsif event_name == 'cancel'
-
'fa-stop'
-
elsif event_name == 'not_authorize'
-
'fa-ban'
-
elsif event_name == 're_start'
-
'fa-play'
-
elsif event_name == 'halt'
-
'fa-pause'
-
elsif event_name == 'retract' || event_name == 'reopen'
-
'fa-reply'
-
elsif event_name == 'return' || event_name == 'reject' || event_name == 'unapprove'
-
'fa-chevron-circle-left'
-
elsif event_name == 'approve'
-
'fa-plus-square'
-
elsif event_name == 'approve_via_transfer'
-
'fa-chevron-circle-right'
-
else
-
''
-
end
-
end
-
-
-
# ======================= state_machine setup =======================
-
-
# Make sure the machine gets initialized so the initial state gets set properly
-
1
def initialize(*)
-
61
super
-
61
machine
-
end
-
-
# Create a state machine for this instance dynamically based on the
-
# transitions defined from the source above
-
1
def machine
-
68
workflow_instance = self
-
68
unless workflow_instance.class.transam_workflow_transitions.empty?
-
13
@machine ||= Machine.new(workflow_instance, initial: (workflow_instance.read_attribute(:state) || workflow_instance.class.transam_workflow_transitions.first[:from_state]), action: :save) do
-
-
6
workflow_instance.class.transam_workflow_transitions.each do |attrs|
-
12
if attrs[:event_name].present? && attrs[:from_state].present? && attrs[:to_state].present?
-
12
transition_attrs = {attrs[:from_state] => attrs[:to_state]}
-
12
event_attrs = attrs[:human_name] ? {human_name: attrs[:human_name]} : {}
-
12
if attrs[:guard].present?
-
if attrs[:guard].is_a?(Hash)
-
transition_attrs[:if] = Proc.new { attrs[:guard].map{|k,v| workflow_instance.state.to_s == k.to_s && workflow_instance.send(v)}.any? }
-
else
-
transition_attrs[:if] = Proc.new { workflow_instance.send(attrs[:guard]) }
-
end
-
end
-
-
12
state(attrs[:from_state], {human_name: attrs[:from_state_human_name]}) if attrs[:from_state_human_name].present?
-
12
state(attrs[:to_state], {human_name: attrs[:to_state_human_name]}) if attrs[:to_state_human_name].present?
-
-
24
event(attrs[:event_name], event_attrs) { branches << transition(transition_attrs) }
-
end
-
end
-
-
6
before_transition do |this_machine, this_transition|
-
this_machine.machine_before_transition(this_transition)
-
end
-
6
after_transition do |this_machine, this_transition|
-
this_machine.machine_after_transition(this_transition)
-
end
-
end
-
end
-
end
-
-
end
-
1
class ConditionEstimationType < ActiveRecord::Base
-
-
# All types that are available
-
1
scope :active, -> { where(:active => true) }
-
-
1
def to_s
-
1
name
-
end
-
-
end
-
1
class ConditionRollupCalculationType < ActiveRecord::Base
-
-
# All types that are available
-
1
scope :active, -> { where(:active => true) }
-
-
1
def to_s
-
name
-
end
-
-
end
-
1
class ConditionType < ActiveRecord::Base
-
-
# All types that are available
-
1
scope :active, -> { where(:active => true) }
-
-
1
def self.max_rating
-
51
order("rating_ceiling DESC").first.rating_ceiling
-
end
-
1
def self.min_rating
-
19
order("rating_ceiling ASC").first.rating_ceiling + 0.01
-
end
-
-
# Uses FTA's condition assessment ratings
-
1
def self.from_rating(estimated_rating)
-
31
if estimated_rating.nil? || estimated_rating < 0.0 || estimated_rating > 5.0
-
1
rating = 0
-
else
-
30
rating = estimated_rating
-
end
-
31
ConditionType.order("rating_ceiling ASC").where("rating_ceiling >= ?", rating).first
-
end
-
-
1
def to_s
-
9
name
-
end
-
-
end
-
1
class ConditionTypePercent < ActiveRecord::Base
-
-
#-----------------------------------------------------------------------------
-
# Callbacks
-
#-----------------------------------------------------------------------------
-
1
after_initialize :set_defaults
-
-
#-----------------------------------------------------------------------------
-
# Associations
-
#-----------------------------------------------------------------------------
-
# Every sign_order_sign belongs to a sign
-
1
belongs_to :condition_update_event,
-
:class_name => 'ConditionUpdateEvent',
-
:foreign_key => "asset_event_id",
-
:inverse_of => :condition_type_percents
-
-
# Every asset_event_subsystem belongs to a subsystem
-
1
belongs_to :condition_type
-
-
#-----------------------------------------------------------------------------
-
# Scopes
-
#-----------------------------------------------------------------------------
-
-
#-----------------------------------------------------------------------------
-
# Validations
-
#-----------------------------------------------------------------------------
-
1
validates :condition_type, :presence => true
-
1
validates :condition_update_event, :presence => true
-
1
validates :pcnt, :numericality => {:only_integer => true, :greater_than_or_equal_to => 0, :less_than_or_equal_to => 100}, allow_nil: true
-
-
#-----------------------------------------------------------------------------
-
# Constants
-
#-----------------------------------------------------------------------------
-
-
# List of allowable form param hash keys
-
1
FORM_PARAMS = [
-
:id,
-
:condition_type_id,
-
:asset_event_id,
-
:pcnt
-
]
-
-
#-----------------------------------------------------------------------------
-
#
-
# Class Methods
-
#
-
#-----------------------------------------------------------------------------
-
-
1
def self.allowable_params
-
2
FORM_PARAMS
-
end
-
-
#-----------------------------------------------------------------------------
-
#
-
# Instance Methods
-
#
-
#-----------------------------------------------------------------------------
-
-
-
#-----------------------------------------------------------------------------
-
#
-
# Protected Methods
-
#
-
#-----------------------------------------------------------------------------
-
1
protected
-
-
# Set resonable defaults for a new instance
-
1
def set_defaults
-
7
self.pcnt ||= 0
-
end
-
end
-
#
-
# Condition update event. This is event type is required for
-
# all implementations
-
#
-
1
class ConditionUpdateEvent < AssetEvent
-
-
# Callbacks
-
1
after_initialize :set_defaults
-
-
# check policy
-
1
after_save :check_policy
-
1
after_destroy :check_policy
-
-
# Associations
-
1
has_many :condition_type_percents, :foreign_key => "asset_event_id", :inverse_of => :condition_update_event, :dependent => :destroy
-
1
accepts_nested_attributes_for :condition_type_percents, :allow_destroy => true, :reject_if => lambda{ |attrs| attrs[:condition_type].blank? }
-
-
-
# Condition of the asset
-
1
belongs_to :condition_type
-
-
1
validates :condition_type, :presence => true
-
1
validates :assessed_rating,
-
:presence => true,
-
:numericality => {
-
:greater_than_or_equal_to => ConditionType.min_rating || 0,
-
:less_than_or_equal_to => ConditionType.max_rating || 5
-
},
-
:allow_nil => true
-
# validates :comments, :length => {:maximum => ???} # There is no limit in the Database/Schema
-
-
1
before_validation do
-
36
self.condition_type ||= ConditionType.from_rating(assessed_rating) unless assessed_rating.blank?
-
end
-
-
#------------------------------------------------------------------------------
-
# Scopes
-
#------------------------------------------------------------------------------
-
# set the default scope
-
141
default_scope { where(:asset_event_type => asset_event_type).order(:event_date, :created_at) }
-
-
# List of hash parameters allowed by the controller
-
1
FORM_PARAMS = [
-
:condition_type_id,
-
:assessed_rating,
-
:condition_type_percents_attributes => [ConditionTypePercent.allowable_params]
-
]
-
-
#------------------------------------------------------------------------------
-
#
-
# Class Methods
-
#
-
#------------------------------------------------------------------------------
-
-
1
def self.allowable_params
-
4
FORM_PARAMS
-
end
-
-
#returns the asset event type for this type of event
-
1
def self.asset_event_type
-
206
AssetEventType.find_by_class_name(self.name)
-
end
-
-
#------------------------------------------------------------------------------
-
#
-
# Instance Methods
-
#
-
#------------------------------------------------------------------------------
-
-
# usually no conditions on can create but can be overridden by specific asset events
-
1
def can_update?
-
asset_event_type.active #&& transam_asset.dependents.count == 0 ## temporaritly disable since not putting condition rollup in UI yet
-
end
-
-
# Override numeric setters to remove any extraneous formats from the number strings eg $, etc.
-
1
def assessed_rating=(num)
-
60
self[:assessed_rating] = sanitize_to_float(num) unless num.blank?
-
end
-
-
# This must be overriden otherwise a stack error will occur
-
1
def get_update
-
3
if condition_type.nil?
-
"Condition recorded as #{sprintf("%#.2f", assessed_rating)}"
-
else
-
3
"Condition recorded as #{sprintf("%#.2f", assessed_rating)} (#{condition_type})"
-
end
-
end
-
-
1
protected
-
-
# Set resonable defaults for a new condition update event
-
# Should be overridden by any form fields during save
-
1
def set_defaults
-
99
super
-
99
self.assessed_rating ||= transam_asset ? (transam_asset.condition_updates.last.try(:reported_condition_rating)) : ConditionType.max_rating
-
99
self.asset_event_type ||= AssetEventType.find_by_class_name(self.name)
-
end
-
-
-
1
def check_policy
-
66
if base_transam_asset
-
1
base_transam_asset.send(:check_policy_rule)
-
1
base_transam_asset.send(:update_asset_state)
-
end
-
end
-
end
-
1
class CostCalculationType < ActiveRecord::Base
-
-
# All types that are available
-
1
scope :active, -> { where(:active => true) }
-
-
1
def to_s
-
1
name
-
end
-
-
end
-
1
class Customer < ActiveRecord::Base
-
-
#associations
-
1
belongs_to :license_type
-
1
has_one :license_holder, -> { where('license_holder = true')}, :class_name => 'Organization'
-
1
has_many :organizations
-
-
#attr_accessible :license_type_id, :active
-
-
# Allow selection of active instances
-
1
scope :active, -> { where(:active => true) }
-
-
1
def to_s
-
1
name
-
end
-
-
end
-
#-------------------------------------------------------------------------------
-
#
-
# DelayedJobPriority
-
#
-
# Configure priority for each job class.
-
#
-
#-------------------------------------------------------------------------------
-
1
class DelayedJobPriority < ActiveRecord::Base
-
1
after_initialize :set_defaults
-
-
#-----------------------------------------------------------------------------
-
# Validations
-
#-----------------------------------------------------------------------------
-
1
validates :class_name, :presence => true, :uniqueness => true
-
1
validates :priority, :presence => true, numericality: { only_integer: true }
-
-
1
private
-
-
1
def set_defaults
-
3
self.priority = 0 unless self.priority.present?
-
end
-
end
-
1
class DispositionType < ActiveRecord::Base
-
-
# All types that are available
-
1
scope :active, -> { where(:active => true) }
-
-
1
def self.search(text, exact = true)
-
2
if exact
-
1
x = where('name = ? OR code = ? OR description = ?', text, text, text).first
-
else
-
1
val = "%#{text}%"
-
1
x = where('name LIKE ? OR code LIKE ? OR description LIKE ?', val, val, val).first
-
end
-
2
x
-
end
-
-
1
def to_s
-
3
name
-
end
-
-
end
-
# Disposition update event. This is event type is required for
-
# all implementations
-
#
-
1
class DispositionUpdateEvent < AssetEvent
-
-
# Callbacks
-
1
after_initialize :set_defaults
-
1
after_save :update_asset
-
-
# Associations
-
-
# Disposition of the asset
-
1
belongs_to :disposition_type
-
-
1
validates :disposition_type, :presence => true
-
-
#------------------------------------------------------------------------------
-
# Scopes
-
#------------------------------------------------------------------------------
-
# set the default scope
-
10
default_scope { where(:asset_event_type_id => AssetEventType.find_by_class_name(self.name).id).order(:event_date, :created_at) }
-
-
# List of hash parameters allowed by the controller
-
1
FORM_PARAMS = [
-
:disposition_type_id
-
]
-
-
#------------------------------------------------------------------------------
-
#
-
# Class Methods
-
#
-
#------------------------------------------------------------------------------
-
-
1
def self.allowable_params
-
3
FORM_PARAMS
-
end
-
-
#returns the asset event type for this type of event
-
1
def self.asset_event_type
-
9
AssetEventType.find_by_class_name(self.name)
-
end
-
-
#------------------------------------------------------------------------------
-
#
-
# Instance Methods
-
#
-
#------------------------------------------------------------------------------
-
-
1
def get_update
-
"#{disposition_type} on #{event_date}"
-
end
-
-
#------------------------------------------------------------------------------
-
#
-
# Protected Methods
-
#
-
#------------------------------------------------------------------------------
-
1
protected
-
-
# Set resonable defaults for a new condition update event
-
1
def set_defaults
-
3
super
-
3
self.disposition_type ||= transam_asset.disposition_updates.last.try(:disposition_type)
-
3
self.asset_event_type ||= AssetEventType.find_by_class_name(self.name)
-
end
-
-
1
def update_asset
-
AssetDispositionUpdateJob.new(transam_asset.object_key).perform
-
end
-
-
end
-
1
class District < ActiveRecord::Base
-
-
# Associations
-
1
belongs_to :district_type
-
-
1
validates :name, :presence => true
-
#validates :code, :presence => true, :uniqueness => true
-
1
validates :description, :presence => true
-
1
validates :district_type_id, :presence => true
-
-
# All types that are available
-
1
scope :active, -> { where(:active => true) }
-
-
3
default_scope { order(:district_type_id, :name) }
-
-
1
DistrictType.active.each do |district_type|
-
scope (district_type.name.parameterize(separator: '_')+'_districts').to_sym, -> { where(district_type: district_type) }
-
end
-
-
1
def to_s
-
"#{name} (#{district_type})"
-
end
-
-
1
def self.search(text, exact = true)
-
if exact
-
x = where('name = ? OR code = ? OR description = ?', text, text, text).first
-
else
-
val = "%#{text}%"
-
x = where('name LIKE ? OR code LIKE ? OR description LIKE ?', val, val, val).first
-
end
-
x
-
end
-
-
end
-
1
class DistrictType < ActiveRecord::Base
-
-
1
has_many :districts
-
-
# All types that are available
-
2
scope :active, -> { where(:active => true) }
-
-
1
def to_s
-
name
-
end
-
-
end
-
#-------------------------------------------------------------------------------
-
#
-
# Document
-
#
-
# A document that has been associated with another class such as an Asset etc. This is a
-
# polymorphic class that can store documents against any class that includes a
-
# documentable association
-
#
-
# To use this class as an association with another class include the following line into
-
# the model
-
#
-
# has_many :documents, :as => :documentable
-
#
-
#-------------------------------------------------------------------------------
-
-
# Include the FileSizevalidator mixin
-
1
require 'file_size_validator'
-
-
1
class Document < ActiveRecord::Base
-
-
# From system config. This is the maximum document size that can be uploaded
-
1
MAX_UPLOAD_FILE_SIZE = Rails.application.config.max_upload_file_size
-
-
# Include the object key mixin
-
1
include TransamObjectKey
-
-
# Use the carrierway uploader
-
1
mount_uploader :document, DocumentUploader
-
-
# Callbacks
-
1
after_initialize :set_defaults
-
1
before_save :update_file_attributes
-
-
# Associations
-
1
belongs_to :documentable, :polymorphic => true
-
-
# Each comment was created by a user
-
1
belongs_to :creator, -> { unscope(where: :active) }, :class_name => "User", :foreign_key => "created_by_id"
-
-
72
default_scope { order('created_at DESC') }
-
-
1
validates :description, :presence => true
-
1
validates :original_filename, :presence => true
-
1
validates :document, :presence => true, :file_size => { :maximum => MAX_UPLOAD_FILE_SIZE.megabytes.to_i }
-
1
validates :created_by_id, :presence => true
-
-
# List of hash parameters allowed by the controller
-
1
FORM_PARAMS = [
-
:documentable_id,
-
:documentable_type,
-
:document,
-
:description,
-
:original_filename,
-
:content_type,
-
:file_size,
-
:created_by_id
-
]
-
-
1
SEARCHABLE_FIELDS = [
-
:object_key,
-
:description,
-
:original_filename,
-
:content_type
-
]
-
-
#-----------------------------------------------------------------------------
-
#
-
# Class Methods
-
#
-
#-----------------------------------------------------------------------------
-
-
1
def self.allowable_params
-
8
FORM_PARAMS
-
end
-
-
#-----------------------------------------------------------------------------
-
#
-
# Instance Methods
-
#
-
#-----------------------------------------------------------------------------
-
-
1
def to_s
-
2
name
-
end
-
-
1
def name
-
4
original_filename
-
end
-
-
1
def searchable_fields
-
1
SEARCHABLE_FIELDS
-
end
-
-
# Return the organization of the owning object so instances can be index using
-
# the keyword indexer
-
1
def organization
-
2
if documentable.respond_to? :organization
-
1
documentable.organization
-
else
-
1
creator.organization
-
end
-
end
-
-
#-----------------------------------------------------------------------------
-
#
-
# Protected Methods
-
#
-
#-----------------------------------------------------------------------------
-
1
protected
-
-
# Set resonable defaults for a new asset event
-
1
def set_defaults
-
-
end
-
-
#-----------------------------------------------------------------------------
-
#
-
# Private Methods
-
#
-
#-----------------------------------------------------------------------------
-
1
private
-
-
1
def update_file_attributes
-
-
23
if document.present? && document_changed?
-
17
self.content_type = document.file.content_type
-
17
self.file_size = document.file.size
-
end
-
end
-
-
end
-
1
class DualFuelType < ActiveRecord::Base
-
-
1
belongs_to :primary_fuel_type, :class_name => "FuelType"
-
1
belongs_to :secondary_fuel_type, :class_name => "FuelType"
-
-
-
1
validates :primary_fuel_type, :presence => true
-
1
validates :secondary_fuel_type, :presence => true
-
-
# All types that are available
-
1
scope :active, -> { where(:active => true) }
-
-
1
def to_s
-
"#{primary_fuel_type.name}-#{secondary_fuel_type.name}"
-
end
-
-
end
-
# Early disposition request event.
-
# Used to request to early dispose an asset
-
#
-
-
# Include the FileSizevalidator mixin
-
1
require 'file_size_validator'
-
-
1
class EarlyDispositionRequestUpdateEvent < AssetEvent
-
-
# Include the Workflow module
-
1
include TransamWorkflow
-
-
# From system config. This is the maximum document size that can be uploaded
-
1
MAX_UPLOAD_FILE_SIZE = Rails.application.config.max_upload_file_size
-
-
# Use the carrierway uploader
-
1
mount_uploader :document, DocumentUploader
-
-
# Callbacks
-
1
after_initialize :set_defaults
-
-
# Validations
-
1
validates :comments, :presence => true
-
1
validates :document, :file_size => { :maximum => MAX_UPLOAD_FILE_SIZE.megabytes.to_i }
-
-
#------------------------------------------------------------------------------
-
# Scopes
-
#------------------------------------------------------------------------------
-
# set the default scope
-
61
default_scope { where(:asset_event_type_id => AssetEventType.find_by_class_name(self.name).id).order(:event_date, :created_at) }
-
# tasks which are currently active based on the workflow
-
1
scope :active, -> { where(state: 'new') }
-
-
# List of hash parameters allowed by the controller
-
1
FORM_PARAMS = [
-
:state,
-
:document
-
]
-
-
#------------------------------------------------------------------------------
-
#
-
# State Machine
-
#
-
# Used to track the state of an early dispostion request event
-
#
-
#------------------------------------------------------------------------------
-
1
state_machine :state, :initial => :new do
-
-
#-------------------------------
-
# List of allowable states
-
#-------------------------------
-
-
# initial state. All are created in this state
-
1
state :new
-
-
# request is approved
-
1
state :approved
-
-
# request is rejected
-
1
state :rejected
-
-
# request is conditionally approved, only allow transferring the asset to another agency
-
1
state :transfer_approved
-
-
#---------------------------------------------------------------------------
-
# List of allowable events. Events transition a request event from one state to another
-
#---------------------------------------------------------------------------
-
-
# approve a request
-
1
event :approve do
-
1
transition :new => :approved
-
end
-
-
# reject a request
-
1
event :reject do
-
1
transition :new => :rejected
-
end
-
-
# conditionally approve a request by only allowing asset transfer to another agency
-
1
event :approve_via_transfer do
-
1
transition :new => :transfer_approved
-
end
-
-
# Callbacks
-
1
before_transition do |request, transition|
-
2
Rails.logger.debug "Transitioning early disposition request #{request.object_key} from #{transition.from_name} to #{transition.to_name} using #{transition.event}"
-
end
-
end
-
-
#------------------------------------------------------------------------------
-
#
-
# Class Methods
-
#
-
#------------------------------------------------------------------------------
-
-
1
def self.allowable_params
-
1
FORM_PARAMS
-
end
-
-
#returns the asset event type for this type of event
-
1
def self.asset_event_type
-
27
AssetEventType.find_by_class_name(self.name)
-
end
-
-
# when state changes, notify related users
-
1
def self.workflow_notification_enabled?
-
3
true
-
end
-
-
#------------------------------------------------------------------------------
-
#
-
# Instance Methods
-
#
-
#------------------------------------------------------------------------------
-
-
1
def get_update
-
1
"Early disposition request was made"
-
end
-
-
1
def get_latest_update
-
4
state_desc = case state.to_s
-
when 'new'
-
1
"made"
-
when 'transfer_approved'
-
1
"approved via transfer"
-
else
-
2
state
-
end
-
-
4
"Early disposition request was #{state_desc}"
-
end
-
-
1
def is_new?
-
1
state == "new"
-
end
-
-
1
def is_unconditional_approved?
-
3
state == "approved"
-
end
-
-
1
def is_approved?
-
2
is_unconditional_approved? || state == "transfer_approved"
-
end
-
-
1
def is_rejected?
-
1
state == "rejected"
-
end
-
-
# default recipients
-
# if :notification_recipients is defined, then :notification_recipients takes precedence
-
1
def default_notification_recipients(event)
-
8
recipients = case event.try(:to_sym)
-
when :new
-
# notify managers
-
1
Role.find_by_name(:manager).try(:users)
-
-
when :reject, :approve_via_transfer, :approve
-
# notify creator
-
7
[creator]
-
else
-
[]
-
end
-
-
8
recipients || []
-
end
-
-
1
def event_in_passive_tense(event)
-
8
case event.try(:to_sym)
-
when :new
-
1
'created'
-
when :reject
-
1
'rejected'
-
when :approve
-
5
'approved'
-
when :approve_via_transfer
-
1
'approved via transfer'
-
else
-
''
-
end
-
end
-
-
1
def notify_event_by(sender, event)
-
4
event = event.to_sym
-
-
4
event_desc = event_in_passive_tense(event)
-
-
4
event_url = Rails.application.routes.url_helpers.inventory_asset_event_path self.try(Rails.application.config.asset_base_class_name.underscore), self
-
-
4
early_notification = Notification.create(text: "Early disposition request for #{self.send(Rails.application.config.asset_base_class_name.underscore)} #{event_desc}", link: event_url, notifiable_type: 'Organization', notifiable_id: self.send(Rails.application.config.asset_base_class_name.underscore).organization_id)
-
-
-
4
recipients = if self.respond_to?(:notification_recipients)
-
notification_recipients(event)
-
else
-
4
default_notification_recipients(event)
-
end
-
4
(recipients || []).uniq.each do |to_user|
-
4
if to_user && to_user != sender
-
-
4
UserNotification.create(notification: early_notification, user: to_user)
-
-
-
# msg = Message.new
-
# msg.user = sender
-
# msg.organization = sender.try(:organization)
-
# msg.to_user = to_user
-
# msg.subject = "Early disposition request for #{asset.asset_tag} #{event_desc}"
-
# msg.body = "Early disposition request for #{asset.name} has been #{event_desc} by #{sender}. The request can be viewed <a href='#{event_url}'>here</a>"
-
# msg.priority_type = PriorityType.default
-
# msg.save
-
end
-
end
-
end
-
-
#------------------------------------------------------------------------------
-
#
-
# Protected Methods
-
#
-
#------------------------------------------------------------------------------
-
1
protected
-
-
# Set resonable defaults for a new condition update event
-
1
def set_defaults
-
19
super
-
19
self.event_date ||= Date.today
-
19
self.state ||= "new"
-
19
self.asset_event_type ||= AssetEventType.find_by_class_name(self.name)
-
end
-
-
end
-
1
class FileContentType < ActiveRecord::Base
-
-
# All types that are available
-
3
scope :active, -> { where(:active => true) }
-
-
1
def to_s
-
5
name
-
end
-
-
end
-
1
class FileStatusType < ActiveRecord::Base
-
-
# All types that are available
-
1
scope :active, -> { where(:active => true) }
-
-
1
def to_s
-
6
name
-
end
-
-
end
-
#------------------------------------------------------------------------------
-
#
-
# Form
-
#
-
# Represents a (usually) multi-page form that an organization needs to complete
-
# for some regulatory or reporting requirement. Forms are database driven and
-
# depend on classes to drive the actual data collection process. The Form model
-
# is similar to Report and simply registers a Form with the database and makes
-
# it available to users with specific roles.
-
#
-
# The Form model is a simple placeholder to different form-specific controllers
-
#
-
#------------------------------------------------------------------------------
-
1
class Form < ActiveRecord::Base
-
-
# Include the object key mixin
-
1
include TransamObjectKey
-
-
14
default_scope { order(:sort_order) }
-
-
# Allow selection of active instances
-
1
scope :active, -> { where(:active => true) }
-
-
1
def to_s
-
name
-
end
-
-
# Return an array of role names allowed to access this report
-
1
def role_names
-
6
roles.split(',')
-
end
-
-
end
-
1
class FrequencyType < ActiveRecord::Base
-
-
# All types that are available
-
2
scope :active, -> { where(:active => true) }
-
-
1
def self.search(text, exact = true)
-
2
if exact
-
1
x = where('name = ? OR description = ?', text, text).first
-
else
-
1
val = "%#{text}%"
-
1
x = where('name LIKE ? OR description LIKE ?', val, val).first
-
end
-
2
x
-
end
-
-
1
def to_s
-
2
name
-
end
-
-
end
-
1
class FuelType < ActiveRecord::Base
-
-
# All types that are available
-
1
scope :active, -> { where(:active => true) }
-
-
1
def self.search(text, exact = true)
-
2
if exact
-
1
x = where('name = ? OR code = ? OR description = ?', text, text, text).first
-
else
-
1
val = "%#{text}%"
-
1
x = where('name LIKE ? OR code LIKE ? OR description LIKE ?', val, val, val).first
-
end
-
2
x
-
end
-
-
1
def to_s
-
1
"#{code}-#{name}"
-
end
-
-
end
-
#-------------------------------------------------------------------------------
-
#
-
# Image
-
#
-
# An Image(photo) that has been associated with another class such as an Asset etc. This is a
-
# polymorphic class that can store images against any class that includes a
-
# imagable association
-
#
-
# To use this class as an association with another class include the following line into
-
# the model
-
#
-
# has_many :images, :as => :imagable
-
#
-
#-------------------------------------------------------------------------------
-
-
# Include the FileSizevalidator mixin
-
1
require 'file_size_validator'
-
-
1
class Image < ActiveRecord::Base
-
-
# Include the object key mixin
-
1
include TransamObjectKey
-
-
# From system config. This is the maximum document size that can be uploaded
-
1
MAX_UPLOAD_FILE_SIZE = Rails.application.config.max_upload_file_size
-
-
# Use the carrierwave uploader
-
1
mount_uploader :image, ImageUploader
-
-
# Callbacks
-
1
after_initialize :set_defaults
-
1
before_save :update_bearing, :update_file_attributes
-
-
# Associations
-
1
belongs_to :base_imagable, :polymorphic => true
-
1
belongs_to :imagable, :polymorphic => true
-
1
belongs_to :image_classification
-
-
# Each comment was created by a user
-
1
belongs_to :creator, -> { unscope(where: :active) }, :class_name => "User", :foreign_key => "created_by_id"
-
-
#validates :description, :presence => true, unless: Proc.new { |i| i.imagable.is_a? User } # User profile photos do not need a description
-
1
validates :original_filename, :presence => true
-
1
validates :image, :presence => true, :file_size => { :maximum => MAX_UPLOAD_FILE_SIZE.megabytes.to_i }
-
1
validates :created_by_id, :presence => true
-
-
285
default_scope { order('created_at DESC') }
-
-
# List of hash parameters allowed by the controller
-
1
FORM_PARAMS = [
-
:base_imagable_type,
-
:base_imagable_id,
-
:global_base_imagable,
-
:imagable_id,
-
:imagable_type,
-
:global_imagable,
-
:image,
-
:image_classification_id,
-
:name,
-
:description,
-
:exportable,
-
:original_filename,
-
:content_type,
-
:file_size,
-
:created_at,
-
:created_by_id,
-
:compass_point,
-
:latitude,
-
:longitude
-
]
-
-
# List of fields which can be searched using a simple text-based search
-
1
SEARCHABLE_FIELDS = [
-
:object_key,
-
:original_filename,
-
:description,
-
:content_type
-
]
-
-
# List of compass points to calculate bearing
-
1
COMPASS_POINTS = %w[N NE E SE S SW W NW]
-
-
-
#-----------------------------------------------------------------------------
-
#
-
# Class Methods
-
#
-
#-----------------------------------------------------------------------------
-
-
1
def self.allowable_params
-
8
FORM_PARAMS
-
end
-
-
#-----------------------------------------------------------------------------
-
#
-
# Instance Methods
-
#
-
#-----------------------------------------------------------------------------
-
-
1
def to_s
-
2
name
-
end
-
-
1
def name
-
6
read_attribute(:name).present? ? read_attribute(:name) : original_filename
-
end
-
-
1
def searchable_fields
-
1
SEARCHABLE_FIELDS
-
end
-
-
# Return the organization of the owning object so instances can be index using
-
# the keyword indexer
-
1
def organization
-
2
if imagable.respond_to? :organization
-
1
imagable.organization
-
else
-
1
creator.organization
-
end
-
end
-
-
# https://neanderslob.com/2015/11/03/polymorphic-associations-the-smart-way-using-global-ids/
-
# following this article we set fta_type based on the fta asset class ie the model
-
1
def global_base_imagable
-
self.base_imagable.to_global_id if self.base_imagable.present?
-
end
-
1
def global_base_imagable=(imagable)
-
self.base_imagable=GlobalID::Locator.locate imagable
-
end
-
1
def global_imagable
-
self.imagable.to_global_id if self.imagable.present?
-
end
-
1
def global_imagable=(imagable)
-
self.imagable=GlobalID::Locator.locate imagable
-
end
-
-
#-----------------------------------------------------------------------------
-
#
-
# Protected Methods
-
#
-
#-----------------------------------------------------------------------------
-
1
protected
-
-
# Set resonable defaults for a new asset event
-
1
def set_defaults
-
37
self.exportable = self.exportable.nil? ? false : self.exportable
-
end
-
-
#-----------------------------------------------------------------------------
-
#
-
# Private Methods
-
#
-
#-----------------------------------------------------------------------------
-
1
private
-
-
1
def update_bearing
-
25
if self.compass_point_changed?
-
self.bearing = convert_compass_point_to_bearing
-
end
-
25
if self.bearing_changed?
-
self.compass_point = convert_bearing_to_compass_point
-
end
-
end
-
-
1
def update_file_attributes
-
25
if image.present? && image
-
25
self.content_type = image.file.content_type
-
25
self.file_size = image.file.size
-
end
-
end
-
-
1
def convert_bearing_to_compass_point
-
if bearing && (bearing * 10 % 225) == 0
-
COMPASS_POINTS[bearing / 22.5]
-
end
-
end
-
-
1
def convert_compass_point_to_bearing
-
if COMPASS_POINTS.include?(self.compass_point)
-
COMPASS_POINTS.index(self.compass_point) * 22.5
-
end
-
end
-
end
-
1
class ImageClassification < ApplicationRecord
-
1
scope :active, -> { where(:active => true) }
-
1
scope :for_category, ->(category) { where(:category => category) }
-
-
1
def to_s
-
name
-
end
-
end
-
1
class Issue < ActiveRecord::Base
-
-
# Include the object key mixin
-
1
include TransamObjectKey
-
-
# Callbacks
-
1
after_initialize :set_defaults
-
-
# Each issue was created by a user
-
1
belongs_to :creator, -> { unscope(where: :active) }, :class_name => "User", :foreign_key => "created_by_id"
-
-
# Each issue has a type [Bug Suggestion, etc.]
-
1
belongs_to :issue_type
-
-
# Each issue has a web browser type [IE 8, IE 9, IE 10, Chrome, Firefox]
-
1
belongs_to :web_browser_type
-
-
# Each issue has a status [Open, Resolved] this list could grow to include under investigation or rejected which is why it isn't a boolean
-
1
belongs_to :issue_status_type
-
-
-
1
validates :issue_type_id, :presence => true
-
1
validates :web_browser_type_id, :presence => true
-
1
validates :comments, :presence => true
-
1
validates :created_by_id, :presence => true
-
17
validates :resolution_comments, :presence => true, :if => Proc.new { |u| u.issue_status_type_id == 2 }
-
# validates_presence_of :resolution_comments, :if :issue_status == 'Resolved'
-
-
# List of hash parameters allowed by the controller
-
1
FORM_PARAMS = [
-
:issue_type_id,
-
:web_browser_type_id,
-
:comments,
-
:issue_status_type_id,
-
:resolution_comments
-
]
-
-
#------------------------------------------------------------------------------
-
#
-
# Class Methods
-
#
-
#------------------------------------------------------------------------------
-
-
1
def self.allowable_params
-
2
FORM_PARAMS
-
end
-
-
#------------------------------------------------------------------------------
-
#
-
# Protected Methods
-
#
-
#------------------------------------------------------------------------------
-
1
protected
-
-
# Set resonable defaults for a new asset event
-
1
def set_defaults
-
15
self.issue_status_type_id ||= 1
-
end
-
-
#------------------------------------------------------------------------------
-
#
-
# Private Methods
-
#
-
#------------------------------------------------------------------------------
-
1
private
-
-
end
-
1
class IssueStatusType < ActiveRecord::Base
-
-
# All types that are available
-
1
scope :active, -> { where(:active => true) }
-
-
1
def to_s
-
name
-
end
-
-
end
-
1
class IssueType < ActiveRecord::Base
-
-
# All types that are available
-
1
scope :active, -> { where(:active => true) }
-
-
1
def to_s
-
1
name
-
end
-
-
end
-
1
class KeywordSearchIndex < ActiveRecord::Base
-
-
1
belongs_to :organization
-
-
#validates :organization, :presence => true
-
1
validates :object_key, :presence => true
-
1
validates :object_class, :presence => true
-
1
validates :search_text, :presence => true
-
-
1
def to_s
-
1
name
-
end
-
-
# Return the Rails path to this object
-
1
def name
-
3
if object_class == Rails.application.config.asset_base_class_name
-
"inventory_path(:id => '#{object_key}')"
-
else
-
3
"#{object_class.underscore}_path(:id => '#{object_key}')"
-
end
-
-
end
-
-
end
-
1
class LicenseType < ActiveRecord::Base
-
-
# associations
-
1
has_many :customers
-
-
# All types that are available
-
1
scope :active, -> { where(:active => true) }
-
-
1
def to_s
-
1
name
-
end
-
-
end
-
#
-
# Location update event. This is event type is required for
-
# all implementations
-
#
-
1
class LocationUpdateEvent < AssetEvent
-
-
# Callbacks
-
1
after_initialize :set_defaults
-
1
after_save :update_asset
-
-
# Associations
-
-
# Each event has a location_id and a location description
-
1
belongs_to :parent, :class_name => Rails.application.config.asset_base_class_name, :foreign_key => :parent_id
-
-
1
validates :parent, :presence => true
-
-
1
attr_accessor :parent_name
-
-
#------------------------------------------------------------------------------
-
# Scopes
-
#------------------------------------------------------------------------------
-
# set the default scope
-
5
default_scope { where(:asset_event_type_id => AssetEventType.find_by_class_name(self.name).id).order(:event_date) }
-
-
# List of hash parameters allowed by the controller
-
1
FORM_PARAMS = [
-
:parent_key,
-
:parent_name
-
]
-
-
#------------------------------------------------------------------------------
-
#
-
# Class Methods
-
#
-
#------------------------------------------------------------------------------
-
-
1
def self.allowable_params
-
1
FORM_PARAMS
-
end
-
-
#returns the asset event type for this type of event
-
1
def self.asset_event_type
-
2
AssetEventType.find_by_class_name(self.name)
-
end
-
-
#------------------------------------------------------------------------------
-
#
-
# Instance Methods
-
#
-
#------------------------------------------------------------------------------
-
-
# This must be overriden otherwise a stack error will occur
-
1
def get_update
-
"Located at #{parent.asset_subtype} #{parent}" unless parent.nil?
-
end
-
-
1
def parent_key=(object_key)
-
self.parent = TransamAsset.find_by_object_key(object_key)
-
end
-
1
def parent_key
-
2
parent.object_key if parent
-
end
-
-
1
def parent_name
-
3
parent.to_s
-
end
-
-
1
protected
-
-
# Forces an update of an assets location. This performs an update on the record.
-
1
def update_asset
-
-
Rails.logger.debug "Updating the recorded location for asset = #{transam_asset.object_key}"
-
-
if transam_asset.location_updates.empty?
-
transam_asset.location_id = nil
-
else
-
event = transam_asset.location_updates.last
-
transam_asset.location_id = event.parent_id
-
end
-
# save changes to this asset
-
transam_asset.save
-
end
-
-
# Set resonable defaults for a new condition update event
-
1
def set_defaults
-
2
super
-
2
self.parent ||= transam_asset.try(:parent)
-
2
self.asset_event_type ||= AssetEventType.find_by_class_name(self.name)
-
end
-
-
end
-
# https://github.com/state-machines/state_machines#static--dynamic-definitions
-
# Generic class for building state_machines
-
1
class Machine
-
1
def self.new(object, *args, &block)
-
6
machine_class = Class.new
-
6
machine = machine_class.state_machine(*args, &block)
-
6
attribute = machine.attribute
-
6
action = machine.action
-
-
# Delegate attributes
-
6
machine_class.class_eval do
-
11
define_method(:definition) { machine }
-
9
define_method(attribute) { object.send(attribute) }
-
12
define_method("#{attribute}=") {|value| object.send("#{attribute}=", value) }
-
6
define_method(action) { object.send(action) } if action
-
-
6
define_method(:machine_before_transition) do |callback|
-
transam_transition = object.class.transam_workflow_transitions.find{|t| t[:event_name].to_s == callback.event.to_s}
-
object.send(transam_transition[:before]) if transam_transition[:before]
-
end
-
6
define_method(:machine_after_transition) do |callback|
-
transam_transition = object.class.transam_workflow_transitions.find{|t| t[:event_name].to_s == callback.event.to_s}
-
object.send(transam_transition[:after]) if transam_transition[:after]
-
end
-
end
-
-
6
machine_class.new
-
end
-
end
-
1
class MaintenanceType < ActiveRecord::Base
-
-
# All types that are available
-
2
scope :active, -> { where(:active => true) }
-
-
1
def self.search(text, exact = true)
-
2
if exact
-
1
x = where('name = ? OR description = ?', text, text).first
-
else
-
1
val = "%#{text}%"
-
1
x = where('name LIKE ? OR description LIKE ?', val, val).first
-
end
-
2
x
-
end
-
-
1
def to_s
-
3
name
-
end
-
-
end
-
#
-
# Maintenance update event.
-
-
# This event is placed in core but Asset does not include it by default allowing
-
# concrete implementations use it as needed in individual asset classes
-
#
-
1
class MaintenanceUpdateEvent < AssetEvent
-
-
# Callbacks
-
1
after_initialize :set_defaults
-
1
after_save :update_asset
-
-
# Associations
-
-
# Condition of the asset
-
1
belongs_to :maintenance_type
-
-
1
validates :maintenance_type, :presence => true
-
1
validate :monotonically_increasing_mileage
-
-
#------------------------------------------------------------------------------
-
# Scopes
-
#------------------------------------------------------------------------------
-
-
# set the default scope
-
3
default_scope { where(:asset_event_type => asset_event_type).order(:event_date, :created_at) }
-
-
# List of hash parameters allowed by the controller
-
1
FORM_PARAMS = [
-
:maintenance_type_id
-
]
-
-
#------------------------------------------------------------------------------
-
#
-
# Class Methods
-
#
-
#------------------------------------------------------------------------------
-
-
1
def self.allowable_params
-
FORM_PARAMS
-
end
-
-
#returns the asset event type for this type of event
-
1
def self.asset_event_type
-
2
AssetEventType.find_by_class_name(self.name)
-
end
-
-
#------------------------------------------------------------------------------
-
#
-
# Instance Methods
-
#
-
#------------------------------------------------------------------------------
-
-
# This must be overriden otherwise a stack error will occur
-
1
def get_update
-
str = ""
-
str += "#{maintenance_type}" unless maintenance_type.nil?
-
str += "at #{current_mileage} miles" unless current_mileage.blank?
-
str
-
end
-
-
1
protected
-
-
# Set resonable defaults for a new condition update event
-
# Should be overridden by any form fields during save
-
1
def set_defaults
-
2
super
-
2
self.asset_event_type ||= AssetEventType.find_by_class_name(self.name)
-
end
-
-
1
def update_asset
-
if current_mileage.present?
-
typed_asset = TransamAsset.get_typed_asset(transam_asset)
-
typed_asset.mileage_updates.create(event_date: self.event_date, current_mileage: self.current_mileage) if (typed_asset.respond_to? :mileage_updates)
-
end
-
end
-
-
# Ensure that the mileage is between the previous (if any) and the following (if any)
-
# Mileage must increase OR STAY THE SAME over time
-
1
def monotonically_increasing_mileage
-
if transam_asset
-
previous_mileage_update = transam_asset.asset_events
-
.where.not(current_mileage: nil)
-
.where("event_date < ? OR (event_date = ? AND created_at < ?)", self.event_date, self.event_date, (self.new_record? ? Time.current : self.created_at) ) # Define a window that runs up to this event
-
.where('object_key != ?', self.object_key)
-
.order(:event_date, :created_at => :asc).last
-
next_mileage_update = transam_asset.asset_events
-
.where.not(current_mileage: nil)
-
.where('event_date > ? OR (event_date = ? AND created_at > ?)', self.event_date, self.event_date, (self.new_record? ? Time.current : self.created_at )) # Define a window that backs up to this event
-
.where('object_key != ?', self.object_key)
-
.order(:event_date, :created_at => :desc).first
-
-
if previous_mileage_update
-
errors.add(:current_mileage, "can't be less than last update (#{previous_mileage_update.current_mileage})") if current_mileage < previous_mileage_update.current_mileage
-
end
-
if next_mileage_update
-
errors.add(:current_mileage, "can't be more than next update (#{next_mileage_update.current_mileage})") if current_mileage > next_mileage_update.current_mileage
-
end
-
end
-
end
-
-
end
-
1
class Manufacturer < ActiveRecord::Base
-
-
1
has_many :assets
-
1
has_many :transam_assets
-
-
# default scope
-
20
default_scope { order('code') }
-
-
# Manufacturers that are maked as active
-
1
scope :active, -> { where(:active => true) }
-
1
scope :active_for_asset_type, -> (asset_type) { active.where("filter = ?", asset_type.class_name) }
-
-
-
1
def self.search(text, filter, exact = true)
-
4
if exact
-
2
x = where('(name = ? OR code = ?) AND filter = ?', text, text, filter)
-
else
-
2
val = "%#{text}%"
-
2
x = where('(name LIKE ? OR code LIKE ?) AND filter = ?', val, val, filter)
-
end
-
4
x.first
-
end
-
-
-
1
def asset_count(org)
-
1
Asset.where(:organization_id => org.id, :manufacturer_id => id).count
-
end
-
-
1
def full_name
-
1
name
-
end
-
-
1
def to_s
-
3
"#{code} - #{name}"
-
end
-
-
end
-
1
class ManufacturerModel < ApplicationRecord
-
-
1
scope :active, -> { where(:active => true) }
-
-
1
def to_s
-
name
-
end
-
-
end
-
1
class Message < ActiveRecord::Base
-
-
# Include the object key mixin
-
1
include TransamObjectKey
-
-
#------------------------------------------------------------------------------
-
# Callbacks
-
#------------------------------------------------------------------------------
-
1
after_initialize :set_defaults
-
1
after_create :send_email
-
-
# Associations
-
1
belongs_to :organization
-
7
belongs_to :user, -> { unscope(where: :active) }
-
8
belongs_to :to_user, -> { unscope(where: :active) }, :class_name => 'User', :foreign_key => "to_user_id"
-
1
belongs_to :priority_type
-
1
belongs_to :message_template
-
1
has_many :responses, :class_name => "Message", :foreign_key => "thread_message_id"
-
-
# Has been tagged by the user
-
1
has_many :message_tags
-
1
has_many :users, :through => :message_tags
-
-
# Validations on core attributes
-
1
validates :organization_id, :presence => true
-
1
validates :user, :presence => true
-
1
validates :to_user, :presence => true
-
1
validates :priority_type_id, :presence => true
-
1
validates :subject, :presence => true
-
1
validates :body, :presence => true
-
-
87
default_scope { where(active: true).order('created_at DESC') }
-
-
# List of allowable form param hash keys
-
1
FORM_PARAMS = [
-
:organization_id,
-
:user_id,
-
:to_user_id,
-
:thread_message_id,
-
:priority_type_id,
-
:subject,
-
:body
-
]
-
-
1
EMAIL_STATUS_DEFAULT = "Stopped"
-
1
EMAIL_STATUS_SENT = "Sent"
-
-
1
def self.allowable_params
-
1
FORM_PARAMS
-
end
-
-
# Returns true if the user has tagged this order
-
1
def tagged? user
-
12
users.include? user
-
end
-
-
# Tags this message for the user
-
1
def tag user
-
1
unless tagged? user
-
1
users << user
-
end
-
end
-
-
# Recursively determine how many total responses there are to this thread
-
1
def response_count
-
9
sum = 0
-
9
responses.each do |r|
-
5
sum += 1
-
5
sum += r.response_count
-
end
-
9
return sum
-
end
-
-
# Set resonable defaults for a new message
-
1
def set_defaults
-
76
self.email_status ||= EMAIL_STATUS_DEFAULT
-
76
self.active = self.active.nil? ? true : self.active
-
end
-
-
1
def email_enabled?
-
31
to_user.try(:notify_via_email) && (!message_template || message_template.active && message_template.email_enabled)
-
end
-
-
# If the to_user has elected to receive emails, send them upon message creation
-
1
def send_email
-
29
if email_enabled?
-
1
Delayed::Job.enqueue SendMessageAsEmailJob.new(object_key), :priority => 0
-
end
-
end
-
-
# def to_user
-
# User.unscope(where: :active).find_by(id: to_user_id)
-
# end
-
-
1
def as_json(options={})
-
2
{
-
object_key: object_key,
-
subject: subject,
-
active: active,
-
created_at: created_at,
-
email_status: email_status,
-
user: user&.to_s,
-
to_user: to_user&.to_s,
-
name: message_template&.name,
-
description: message_template&.description,
-
message_enabled: active && message_template&.message_enabled,
-
2
email_enabled: email_status == EMAIL_STATUS_SENT || (active && email_enabled?)
-
}
-
end
-
-
end
-
#-------------------------------------------------------------------------------
-
# MessageProxy
-
#
-
# Proxy class for gathering new message data
-
#
-
#-------------------------------------------------------------------------------
-
1
class MessageProxy < Proxy
-
-
#-----------------------------------------------------------------------------
-
# Attributes
-
#-----------------------------------------------------------------------------
-
1
attr_accessor :priority_type_id
-
1
attr_accessor :to_user_ids
-
1
attr_accessor :group_roles
-
1
attr_accessor :group_agencys
-
1
attr_accessor :available_agencies
-
1
attr_accessor :subject
-
1
attr_accessor :body
-
1
attr_accessor :send_to_group
-
-
#-----------------------------------------------------------------------------
-
# Validations
-
#-----------------------------------------------------------------------------
-
1
validates :priority_type_id, :presence => true
-
1
validates :subject, :presence => true
-
1
validates :body, :presence => true
-
-
1
validate :at_least_one_target_user
-
-
#-----------------------------------------------------------------------------
-
# Constants
-
#-----------------------------------------------------------------------------
-
-
# List of allowable form param hash keys
-
1
FORM_PARAMS = [
-
:priority_type_id,
-
:subject,
-
:body,
-
:send_to_group,
-
:to_user_ids => [],
-
:group_roles => [],
-
:group_agencys => []
-
]
-
-
#-----------------------------------------------------------------------------
-
#
-
# Class Methods
-
#
-
#-----------------------------------------------------------------------------
-
-
1
def self.allowable_params
-
1
FORM_PARAMS
-
end
-
-
#-----------------------------------------------------------------------------
-
#
-
# Instance Methods
-
#
-
#-----------------------------------------------------------------------------
-
-
#-----------------------------------------------------------------------------
-
#
-
# Protected Methods
-
#
-
#-----------------------------------------------------------------------------
-
1
protected
-
-
1
def at_least_one_target_user
-
7
if send_to_group == '0'
-
5
user_ids = []
-
9
to_user_ids.each {|x| user_ids << x unless x.blank?}
-
5
@errors.add(:to_user_ids, "At least one user must be selected") if user_ids.empty?
-
else
-
2
group_ids = []
-
3
group_roles.each {|x| group_ids << x unless x.blank?}
-
2
group_agencys.each {|x| group_ids << x unless x.blank?}
-
2
@errors.add(:base, "At least one organization or user role must be selected") if group_ids.empty?
-
end
-
end
-
-
1
def initialize(attrs = {})
-
8
super
-
8
attrs.each do |k, v|
-
20
self.send "#{k}=", v
-
end
-
8
self.send_to_group ||= '0'
-
8
self.to_user_ids ||= []
-
8
self.group_roles ||= []
-
8
self.group_agencys ||= []
-
end
-
-
end
-
#-------------------------------------------------------------------------------
-
#
-
# Message Tag
-
#
-
# Map relation that maps a message to a user as part of a tag.
-
#
-
#-------------------------------------------------------------------------------
-
1
class MessageTag < ActiveRecord::Base
-
#-----------------------------------------------------------------------------
-
# Callbacks
-
#-----------------------------------------------------------------------------
-
-
#-----------------------------------------------------------------------------
-
# Associations
-
#-----------------------------------------------------------------------------
-
# Every message_tag belongs to a message
-
1
belongs_to :message
-
-
# Every message_tag belongs to a user
-
1
belongs_to :user
-
-
#-----------------------------------------------------------------------------
-
# Scopes
-
#-----------------------------------------------------------------------------
-
-
#-----------------------------------------------------------------------------
-
# Validations
-
#-----------------------------------------------------------------------------
-
1
validates :message, :presence => true
-
1
validates :user, :presence => true
-
-
#-----------------------------------------------------------------------------
-
# Constants
-
#-----------------------------------------------------------------------------
-
-
# List of allowable form param hash keys
-
1
FORM_PARAMS = [
-
]
-
-
#-----------------------------------------------------------------------------
-
#
-
# Class Methods
-
#
-
#-----------------------------------------------------------------------------
-
-
1
def self.allowable_params
-
1
FORM_PARAMS
-
end
-
-
#-----------------------------------------------------------------------------
-
#
-
# Instance Methods
-
#
-
#-----------------------------------------------------------------------------
-
-
#-----------------------------------------------------------------------------
-
#
-
# Protected Methods
-
#
-
#-----------------------------------------------------------------------------
-
1
protected
-
-
end
-
1
class MessageTemplate < ActiveRecord::Base
-
-
# Include the object key mixin
-
1
include TransamObjectKey
-
-
#------------------------------------------------------------------------------
-
# Callbacks
-
#------------------------------------------------------------------------------
-
1
after_initialize :set_defaults
-
1
after_create :notify_template_changes
-
1
after_save :template_changed
-
-
# Associations
-
1
belongs_to :priority_type
-
-
# Validations on core attributes
-
1
validates :priority_type_id, :presence => true
-
1
validates :name, :presence => true
-
1
validates :subject, :presence => true
-
1
validates :delivery_rules, :presence => true
-
1
validates :body, :presence => true
-
-
1
validate :validate_message_or_email_enabled
-
-
1
scope :active, -> { where(active: true) }
-
-
# List of allowable form param hash keys
-
1
FORM_PARAMS = [
-
:priority_type_id,
-
:name,
-
:description,
-
:subject,
-
:body,
-
:delivery_rules,
-
:active,
-
:email_enabled
-
]
-
-
1
def self.allowable_params
-
FORM_PARAMS
-
end
-
-
1
protected
-
-
# Set resonable defaults
-
1
def set_defaults
-
20
self.active = false if self.active.nil?
-
20
self.priority_type_id = PriorityType.default&.id if self.priority_type_id.nil?
-
20
self.message_enabled = true if self.message_enabled.nil?
-
20
self.email_enabled = true if self.email_enabled.nil?
-
20
self.is_system_template = false if self.is_system_template.nil?
-
20
self.is_implemented = false if self.is_implemented.nil?
-
end
-
-
1
def validate_message_or_email_enabled
-
message_enabled || email_enabled
-
end
-
-
1
def notify_template_changes
-
-
-
Delayed::Job.enqueue MessageTemplateInformerJob.new(object_key)
-
end
-
-
1
def template_changed
-
if delivery_rules_changed? || body_changed?
-
update!(is_implemented: false)
-
notify_template_changes
-
end
-
end
-
-
end
-
#------------------------------------------------------------------------------
-
#
-
# Notice
-
#
-
# Represents a business activity that is requried to be completed
-
# Needs standardizing between active vs end_datetime. Views use end_datetime
-
#------------------------------------------------------------------------------
-
1
class Notice < ActiveRecord::Base
-
-
# Include the object key mixin
-
1
include TransamObjectKey
-
-
1
attr_accessor :display_date
-
1
attr_accessor :display_hour
-
1
attr_accessor :end_date
-
1
attr_accessor :end_hour
-
-
#------------------------------------------------------------------------------
-
# Callbacks
-
#------------------------------------------------------------------------------
-
1
after_initialize :set_defaults
-
1
before_validation :calculate_datetimes_from_virtual_attributes
-
-
#------------------------------------------------------------------------------
-
#
-
# Virtual Attributes
-
#
-
# Build the datetimes from a date and an hour, passed as strings
-
#------------------------------------------------------------------------------
-
1
def display_datetime_date
-
display_datetime.to_date if display_datetime
-
end
-
1
def display_datetime_hour
-
3
display_datetime.hour if display_datetime
-
end
-
1
def display_datetime_date=(date_str)
-
1
Rails.logger.debug "in display_datetime_date #{date_str}"
-
1
self.display_date = Chronic.parse(date_str).to_date
-
end
-
1
def display_datetime_hour=(hr_str)
-
1
Rails.logger.debug "in display_datetime_hour #{hr_str}"
-
1
self.display_hour = hr_str
-
end
-
-
1
def end_datetime_date
-
2
end_datetime.to_date if end_datetime
-
end
-
1
def end_datetime_hour
-
3
end_datetime.hour if end_datetime
-
end
-
-
1
def end_datetime_date=(date_str)
-
1
Rails.logger.debug "in end_datetime_date #{date_str}"
-
1
self.end_date = Chronic.parse(date_str).to_date
-
end
-
1
def end_datetime_hour=(hr_str)
-
1
Rails.logger.debug "in end_datetime_hour #{hr_str}"
-
1
self.end_hour = hr_str
-
end
-
-
-
#------------------------------------------------------------------------------
-
# Associations
-
#------------------------------------------------------------------------------
-
-
# Every notice has an optional organizatiopn type that that can see the notice
-
1
belongs_to :organization
-
1
belongs_to :notice_type
-
-
# Every notice must have a defined type
-
1
belongs_to :notice_type
-
-
#------------------------------------------------------------------------------
-
# Validations
-
#------------------------------------------------------------------------------
-
-
1
validates :subject, :presence => true, :length => {:maximum => 64}
-
1
validates :summary, :presence => true, :length => {:maximum => 254}
-
1
validates :display_datetime, :presence => true
-
1
validates :end_datetime, :presence => true
-
1
validates :notice_type, :presence => true
-
1
validate :validate_end_after_start
-
-
# List of allowable form param hash keys
-
1
FORM_PARAMS = [
-
:organization_id,
-
:subject,
-
:summary,
-
:details,
-
:display_icon_name,
-
:display_datetime_date,
-
:display_datetime_hour,
-
:end_datetime_date,
-
:end_datetime_hour,
-
:notice_type_id,
-
:active
-
]
-
-
#------------------------------------------------------------------------------
-
#
-
# Scopes
-
#
-
#------------------------------------------------------------------------------
-
-
# Notices that are maked as active
-
11
scope :active, -> { where(:active => true) }
-
# Notices that have a start time before current date/time and an end time before current date/time
-
7
scope :visible, -> { active.where("? BETWEEN display_datetime AND end_datetime", DateTime.current) }
-
# Notices that are for the entire set of organizations
-
3
scope :system_level_notices, -> { active.visible.where("organization_id IS null") }
-
# Notices that are active and visible for a specific organization
-
3
scope :active_for_organizations, -> (orgs) { active.visible.where("organization_id IS null OR organization_id IN (?)", orgs) }
-
-
#------------------------------------------------------------------------------
-
#
-
# Class Methods
-
#
-
#------------------------------------------------------------------------------
-
-
1
def self.allowable_params
-
2
FORM_PARAMS
-
end
-
-
#------------------------------------------------------------------------------
-
#
-
# Instance Methods
-
#
-
#------------------------------------------------------------------------------
-
-
1
def visible?
-
1
now = Time.now
-
1
if display_datetime < now and now < end_datetime
-
1
true
-
else
-
false
-
end
-
end
-
# Return the duration of a notice's display in hours
-
1
def duration_in_hours
-
3
float_duration = (end_datetime - display_datetime)/3600
-
3
float_duration.ceil # Round up to nearest hour
-
end
-
-
1
def to_s
-
subject
-
end
-
-
#------------------------------------------------------------------------------
-
#
-
# Protected Methods
-
#
-
#------------------------------------------------------------------------------
-
1
protected
-
-
# Set resonable defaults for a new notice
-
# Datetime attributes are set in the following order
-
# 1. Stored DB datetime
-
# 2. Parsed from form_params
-
# 3. Set as defaults (beginning of the next hour, end of today)
-
1
def set_defaults
-
68
self.active = true if self.active.nil?
-
68
self.display_datetime ||= parsed_display_datetime_from_virtual_attributes || (DateTime.current.beginning_of_hour)
-
68
self.end_datetime ||= parsed_end_datetime_from_virtual_attributes || display_datetime.end_of_day
-
end
-
-
# Before validating, ensure that we have converted from virtual attributes
-
# to native ones
-
1
def calculate_datetimes_from_virtual_attributes
-
-
29
self.display_datetime = parsed_display_datetime_from_virtual_attributes || (DateTime.current.beginning_of_hour)
-
29
self.end_datetime = parsed_end_datetime_from_virtual_attributes || display_datetime.end_of_day
-
-
end
-
-
# Returns nil if a bad parse
-
1
def parsed_display_datetime_from_virtual_attributes
-
61
Rails.logger.debug "in parsed_display_datetime_from_virtual_attributes: #{display_date} #{display_hour}"
-
61
dt = Chronic.parse("#{display_date} #{display_hour}", :ambiguous_time_range => :none)
-
61
Rails.logger.debug "parsed value = #{dt}"
-
61
return dt
-
end
-
-
# Returns nil if a bad parse
-
1
def parsed_end_datetime_from_virtual_attributes
-
61
Rails.logger.debug "in parsed_end_datetime_from_virtual_attributes #{end_date} #{end_hour}"
-
61
dt = Chronic.parse("#{end_date} #{end_hour}", :ambiguous_time_range => :none)
-
61
Rails.logger.debug "parsed value = #{dt}"
-
61
return dt
-
end
-
-
1
def validate_end_after_start
-
29
if end_datetime < display_datetime
-
errors.add(:end_datetime, "must be after start time")
-
end
-
end
-
-
end
-
1
class NoticeType < ActiveRecord::Base
-
-
# All types that are available
-
1
scope :active, -> { where(:active => true) }
-
-
1
def to_s
-
2
name
-
end
-
end
-
1
class Notification < ActiveRecord::Base
-
-
1
DEFAULT_INTERVAL = 10000 # checking for notifications every 10 seconds in milliseconds
-
-
# Include the object key mixin
-
1
include TransamObjectKey
-
-
#------------------------------------------------------------------------------
-
# Callbacks
-
#------------------------------------------------------------------------------
-
1
after_initialize :set_defaults
-
-
# Associations
-
1
belongs_to :notifiable, polymorphic: true
-
1
has_many :user_notifications
-
1
has_many :users, through: :user_notifications
-
-
# Validations on core attributes
-
1
validates :text, :presence => true
-
1
validates :link, :presence => true
-
-
7
default_scope { order('created_at DESC') }
-
-
1
scope :active, -> { where(active: true) }
-
-
1
def self.interval
-
1
Rails.application.config.try(:notification_interval) || DEFAULT_INTERVAL
-
end
-
-
1
protected
-
1
def set_defaults
-
6
self.active = self.active.nil? ? true : self.active
-
end
-
end
-
#------------------------------------------------------------------------------
-
#
-
# Organization
-
#
-
# Represents a basic organization in a flat organizational hierarchy
-
# without any relationships to other organizations or assets
-
#
-
#------------------------------------------------------------------------------
-
1
class Organization < ActiveRecord::Base
-
-
#------------------------------------------------------------------------------
-
# Callbacks
-
#------------------------------------------------------------------------------
-
1
after_initialize :set_defaults
-
-
4
scope :active, -> { where(:active => true) }
-
-
#------------------------------------------------------------------------------
-
# Overrides
-
#------------------------------------------------------------------------------
-
#require rails to use the org short name as the restful parameter. All URLS will be of the form
-
# /org/{short_name}/...
-
1
def to_param
-
6
short_name
-
end
-
-
#------------------------------------------------------------------------------
-
# Associations common to all organizations
-
#------------------------------------------------------------------------------
-
-
# Every organization belongs to a customer
-
1
belongs_to :customer
-
-
# Every organization has a class type
-
1
belongs_to :organization_type
-
-
# Every organization can have a set of users
-
1
has_and_belongs_to_many :users, :join_table => 'users_organizations'
-
-
# Every organization can have messages
-
1
has_many :messages
-
-
# Every organization can have a set of uploads
-
1
has_many :uploads
-
-
# Every organization can have 0 or more asset groups
-
1
has_many :asset_groups
-
-
# Every organization can have 0 or more vendors
-
1
has_many :vendors
-
-
# Validations for associations
-
1
validates :customer_id, :presence => true
-
1
validates :organization_type_id, :presence => true
-
-
#------------------------------------------------------------------------------
-
# Attributes common to all organization types
-
#------------------------------------------------------------------------------
-
-
1
validates :name, :presence => true
-
1
validates :short_name, :presence => true, :uniqueness => true
-
1
validates :address1, :presence => true
-
1
validates :city, :presence => true
-
1
validates :state, :presence => true
-
1
validates :zip, :presence => true
-
#validates :license_holder, :presence => true
-
1
validates :phone, :presence => true
-
1
validates :url, :presence => true
-
-
# List of allowable form param hash keys
-
1
FORM_PARAMS = [
-
:customer_id,
-
:organization_type_id,
-
:external_id,
-
:license_holder,
-
:name,
-
:legal_name,
-
:short_name,
-
:country,
-
:address1,
-
:address2,
-
:city,
-
:state,
-
:zip,
-
:county,
-
:phone,
-
:fax,
-
:url,
-
:active
-
]
-
-
#------------------------------------------------------------------------------
-
#
-
# Class Methods
-
#
-
#------------------------------------------------------------------------------
-
-
1
def self.allowable_params
-
3
FORM_PARAMS
-
end
-
-
# returns a typed value of the organization if one exists
-
1
def self.get_typed_organization(org)
-
215
if org
-
215
class_name = org.organization_type.class_name
-
215
klass = Object.const_get class_name
-
215
o = klass.find(org.id)
-
215
return o
-
end
-
end
-
-
#------------------------------------------------------------------------------
-
#
-
# Instance Methods
-
#
-
#------------------------------------------------------------------------------
-
-
# Returns the user with the assigned role :technical_contact
-
1
def technical_contact
-
2
usrs = users_with_role :technical_contact
-
2
if usrs.empty?
-
nil
-
else
-
1
usrs.first
-
end
-
end
-
-
# Returns the users in the organiztion with the assigned role
-
1
def users_with_role role_name
-
4
users.with_role role_name
-
end
-
-
#-----------------------------------------------------------------------------
-
# Returns a hash of asset_type_ids and the counts per non-zero type
-
#-----------------------------------------------------------------------------
-
1
def asset_type_counts(active_only=true)
-
4
if active_only
-
3
Asset.operational.where(:organization => self).group(:asset_type_id).count
-
else
-
1
Asset.where(:organization => self).group(:asset_type_id).count
-
end
-
end
-
-
#-----------------------------------------------------------------------------
-
# Returns a hash of asset subtype ids and the counts per non-zero type
-
#-----------------------------------------------------------------------------
-
1
def asset_subtype_counts(asset_type_id, active_only=true)
-
2
if active_only
-
1
Asset.operational.where(:organization => self, :asset_type_id => asset_type_id).group(:asset_subtype_id).count
-
else
-
1
Asset.where(:organization => self, :asset_type_id => asset_type_id).group(:asset_subtype_id).count
-
end
-
end
-
-
#-----------------------------------------------------------------------------
-
# Returns true if the organization is of the specified class or has the specified class as
-
# and ancestor (superclass).
-
#
-
# usage: a.type_of? type
-
# where type can be one of:
-
# a symbol e.g :grantee
-
# a class name eg Grantee
-
# a string eg "grantee"
-
#
-
#-----------------------------------------------------------------------------
-
1
def type_of?(type)
-
begin
-
self.class.ancestors.include?(type.to_s.classify.constantize)
-
rescue
-
false
-
end
-
end
-
-
1
def coded_name
-
1643
"#{short_name}-#{name}"
-
end
-
-
1
def object_key
-
4
"#{short_name}"
-
end
-
-
1
def to_s
-
1475
short_name
-
end
-
-
# Generic organizations do not own transit assets
-
1
def has_assets?
-
1
false
-
end
-
-
# Returns a policy for this org. This must be overriden for concrete classes
-
1
def get_policy
-
# get a typed version of the organization and return its value
-
16
org = is_typed? ? self : Organization.get_typed_organization(self)
-
16
return org.get_policy unless org.nil?
-
end
-
-
# returns true if the organization instance is strongly typed, i.e., a concrete class
-
# false otherwise.
-
# true
-
1
def is_typed?
-
17
self.class.to_s == organization_type.class_name
-
end
-
-
# Returns the organization sub-heirarchy rooted at this organization. The
-
# method returns this organization and all below it
-
1
def organization_hierarchy
-
a = []
-
# Perform a depth-first traversal of the org tree
-
a << self
-
children = Organization.where(:parent_id => self.id)
-
children.each do |child|
-
a += child.organization_hierarchy
-
end
-
a.uniq
-
end
-
-
#------------------------------------------------------------------------------
-
#
-
# Protected Methods
-
#
-
#------------------------------------------------------------------------------
-
1
protected
-
-
# Set resonable defaults for a new organization
-
1
def set_defaults
-
8237
self.active = self.active.nil? ? true : self.active
-
8237
self.state ||= SystemConfig.instance.try(:default_state_code)
-
end
-
-
end
-
1
class OrganizationRoleMapping < ActiveRecord::Base
-
-
#---------------------------------------------------------------------------
-
# Validations
-
#---------------------------------------------------------------------------
-
-
1
validates :organization_id, :presence => true, :allow_nil => false, :uniqueness => { :scope => :role_id}
-
1
validates :role_id, :presence => true, :allow_nil => false, :uniqueness => { :scope => :organization_id}
-
-
#---------------------------------------------------------------------------
-
# Class Methods
-
#---------------------------------------------------------------------------
-
-
# { org_id => role_id_array}, e.g. {1 => [1, 8, 9]}
-
1
def self.role_ids_by_org
-
1
OrganizationRoleMapping.pluck(:organization_id, :role_id).each_with_object({}) { |(f,l),h| h.update(f=>[l]) {|_,ov,nv| ov+nv }}
-
end
-
-
# { role_id => org_id_array}, e.g. {1 => [1, 8, 9]}
-
1
def self.org_ids_by_role
-
OrganizationRoleMapping.pluck(:role_id, :organization_id).each_with_object({}) { |(f,l),h| h.update(f=>[l]) {|_,ov,nv| ov+nv }}
-
end
-
end
-
1
class OrganizationType < ActiveRecord::Base
-
-
# All types that are available
-
1
scope :active, -> { where(:active => true) }
-
-
1
def to_s
-
1
name
-
end
-
-
# used in the user form interface to determine the types of users for that org type
-
1
def role_mappings
-
Role.where(name: roles.split(','))
-
end
-
end
-
#------------------------------------------------------------------------------
-
#
-
# Policy
-
#
-
#------------------------------------------------------------------------------
-
1
class Policy < ActiveRecord::Base
-
-
# Include the object key mixin
-
1
include TransamObjectKey
-
# Include the numeric sanitizers mixin
-
1
include TransamNumericSanitizers
-
# Include the object key mixin
-
1
include FiscalYear
-
-
#------------------------------------------------------------------------------
-
# Callbacks
-
#------------------------------------------------------------------------------
-
1
after_initialize :set_defaults
-
-
1
after_save :apply_policy
-
-
#------------------------------------------------------------------------------
-
# Associations
-
#------------------------------------------------------------------------------
-
# Every policy belongs to an organization
-
1
belongs_to :organization
-
-
# Every policy can have a parent policy
-
1
belongs_to :parent, :class_name => 'Policy', :foreign_key => :parent_id
-
-
# Has a single method for estimating condition
-
1
belongs_to :condition_estimation_type
-
-
# Has 0 or more asset type rules. These are removed if the policy is removed
-
1
has_many :policy_asset_type_rules, :dependent => :destroy
-
-
# Has 0 or more asset subtype rules. These are removed if the policy is removed
-
1
has_many :policy_asset_subtype_rules, :dependent => :destroy
-
-
#------------------------------------------------------------------------------
-
# Validations
-
#------------------------------------------------------------------------------
-
1
validates :organization, :presence => true
-
1
validates :description, :presence => true
-
1
validates :condition_estimation_type, :presence => true
-
1
validates :condition_threshold, :presence => true, :numericality => {:greater_than_or_equal_to => 0.0, :less_than_or_equal_to => 5.0 }
-
-
#------------------------------------------------------------------------------
-
# Scopes
-
#------------------------------------------------------------------------------
-
-
# default scope
-
-
# set named scopes
-
1
scope :active, -> { where(:active => true) }
-
-
# List of hash parameters allowed by the controller
-
1
FORM_PARAMS = [
-
:organization_id,
-
:description,
-
:condition_estimation_type_id,
-
:condition_threshold,
-
:active
-
]
-
-
#------------------------------------------------------------------------------
-
#
-
# Class Methods
-
#
-
#------------------------------------------------------------------------------
-
-
1
def self.allowable_params
-
1
FORM_PARAMS
-
end
-
-
#------------------------------------------------------------------------------
-
#
-
# Instance Methods
-
#
-
#------------------------------------------------------------------------------
-
1
def condition_threshold=(num)
-
306
self[:condition_threshold] = sanitize_to_float(num)
-
end
-
-
1
def name
-
4
"#{organization.short_name} Policy"
-
end
-
-
1
def to_s
-
1
name
-
end
-
-
# Returns true of the policy has a rule for the asset type. false otherwise
-
1
def asset_type_rule? asset_type
-
2
asset_type_rule(asset_type).present?
-
end
-
# Returns the policy type rule for the asset typoe. Nil if it does not exist
-
1
def asset_type_rule asset_type
-
10
policy_asset_type_rules.find_by(:asset_type_id => asset_type.id)
-
end
-
-
# Returns true of the policy has a rule for the asset subtype. false otherwise`
-
1
def asset_subtype_rule? asset_subtype
-
2
asset_subtype_rule(asset_subtype).present?
-
end
-
# Returns the policy subtype rule for the asset subtype. Nil if it does not exist
-
1
def asset_subtype_rule asset_subtype, fuel_type=nil
-
10
fuel_type.nil? ?
-
policy_asset_subtype_rules.find_by(:asset_subtype_id => asset_subtype.id) :
-
policy_asset_subtype_rules.find_by(:asset_subtype_id => asset_subtype.id, :fuel_type => fuel_type.id)
-
-
end
-
-
# Returns the policy asset type rule for a given asset. If the asset type rule
-
# does not exit in this policy it is created from the parent policy and added
-
# to this policy first and then returned
-
#
-
# Raises a runtime error if the policy is a top level policy (no parent) or the
-
# parent does not contain the rule
-
1
def find_or_create_asset_type_rule asset_type
-
-
5
rule = asset_type_rule asset_type
-
5
if rule.blank?
-
# Check the parent
-
3
if parent.present?
-
2
parent_rule = parent.policy_asset_type_rules.find_by(:asset_type_id => asset_type.id)
-
# Chrck to see of we got a rule
-
2
if parent_rule.present?
-
1
rule = parent_rule.dup
-
1
rule.policy = self
-
1
rule.save
-
else
-
1
raise "Rule for asset type #{asset_type} was not found in the parent policy."
-
end
-
else
-
1
raise "Policy is a top level policy"
-
end
-
end
-
3
rule
-
end
-
-
# Returns the policy asset type rule for a given asset. If the asset type rule
-
# does not exit in this policy it is created from the parent policy and added
-
# to this policy first and then returned
-
#
-
# Raises a runtime error if the policy is a top level policy (no parent) or the
-
# parent does not contain the rule
-
1
def find_or_create_asset_subtype_rule asset_subtype, fuel_type=nil
-
-
5
rule = asset_subtype_rule asset_subtype, fuel_type
-
5
if rule.blank?
-
# Check the parent
-
3
if parent.present?
-
-
2
if fuel_type.present?
-
parent_rule = parent.policy_asset_subtype_rules.find_by(:asset_subtype_id => asset_subtype.id, :fuel_type => fuel_type.id)
-
end
-
-
2
if parent_rule.nil?
-
2
parent_rule = parent.policy_asset_subtype_rules.find_by(:asset_subtype_id => asset_subtype.id)
-
end
-
-
# Check to see of we got a rule
-
2
if parent_rule.present?
-
1
rule = parent_rule.dup
-
1
rule.policy = self
-
-
#If we picked up the default subtype rule then make sure we set the fuel rule here so there is on for this asset now and in the future.
-
1
if fuel_type.present? && rule.fuel_type_id != fuel_type.id
-
rule.fuel_type_id = fuel_type.id
-
end
-
-
1
rule.save
-
-
-
else
-
1
raise "Rule for asset subtype #{asset_subtype} was not found in the parent policy."
-
end
-
else
-
1
raise "Policy is a top level policy."
-
end
-
end
-
3
rule
-
end
-
-
#------------------------------------------------------------------------------
-
#
-
# Protected Methods
-
#
-
#------------------------------------------------------------------------------
-
1
protected
-
-
# Set resonable defaults for a new policy
-
1
def set_defaults
-
469
self.condition_estimation_type_id ||= 1
-
469
self.condition_threshold ||= 2.5
-
end
-
-
1
def apply_policy
-
104
TransamAsset.operational.where(organization_id: self.organization_id).each do |asset|
-
Rails.logger.warn "Issue applying policy on TransAM Asset #{asset}" unless asset.save
-
end
-
end
-
1
handle_asynchronously :apply_policy
-
-
end
-
#-------------------------------------------------------------------------------
-
#
-
# PolicyAssetSubtypeRule
-
#
-
# Policy rule for an asset type for an organiation
-
#
-
#-------------------------------------------------------------------------------
-
1
class PolicyAssetSubtypeRule < ActiveRecord::Base
-
-
# Include the numeric sanitizers mixin
-
1
include TransamNumericSanitizers
-
1
include FiscalYear
-
-
#-----------------------------------------------------------------------------
-
# Callbacks
-
#-----------------------------------------------------------------------------
-
1
after_initialize :set_defaults
-
-
1
after_save :distribute_policy
-
1
after_update_commit :apply_policy
-
1
after_commit :apply_distributed_policy
-
-
#-----------------------------------------------------------------------------
-
# Associations
-
#-----------------------------------------------------------------------------
-
# Every policy rule belongs to a policy, and thus an organization
-
1
belongs_to :policy
-
# Every one of these rules applies to an asset type
-
1
belongs_to :asset_subtype
-
# Every one of these rules applies to an asset type
-
1
belongs_to :replace_asset_subtype, :class_name => 'AssetSubtype', :foreign_key => :replace_asset_subtype_id
-
-
#-----------------------------------------------------------------------------
-
# Validations
-
#-----------------------------------------------------------------------------
-
1
validates :policy, :presence => true
-
1
validates :asset_subtype, :presence => true
-
-
1
validates :min_service_life_months, :presence => true, :numericality => {:only_integer => true, :greater_than_or_equal_to => 0}
-
1
validates :replacement_cost, :presence => true, :numericality => {:only_integer => true, :greater_than_or_equal_to => 0}
-
1
validates :cost_fy_year, :presence => true, :numericality => {:only_integer => true, :greater_than_or_equal_to => 0}
-
1
validates_inclusion_of :replace_with_new, :in => [true, false]
-
1
validates_inclusion_of :replace_with_leased, :in => [true, false]
-
1
validates :lease_length_months, :allow_nil => true, :numericality => {:only_integer => true, :greater_than_or_equal_to => 0}
-
-
1
validates :rehabilitation_service_month, :presence => true, :numericality => {:only_integer => true, :greater_than_or_equal_to => 0}
-
1
validates :rehabilitation_labor_cost, :allow_nil => true, :numericality => {:only_integer => true, :greater_than_or_equal_to => 0}
-
1
validates :rehabilitation_parts_cost, :allow_nil => true, :numericality => {:only_integer => true, :greater_than_or_equal_to => 0}
-
1
validates :extended_service_life_months, :numericality => {:only_integer => true, :greater_than_or_equal_to => 0}
-
-
1
validates :min_used_purchase_service_life_months, :presence => true, :numericality => {:only_integer => true, :greater_than_or_equal_to => 0}
-
-
# Custom validator for checking values against parent policies
-
1
validate :validate_min_allowable_policy_values
-
-
#------------------------------------------------------------------------------
-
# Scopes
-
#------------------------------------------------------------------------------
-
638
default_scope { order(:asset_subtype_id) }
-
-
#-----------------------------------------------------------------------------
-
# List of hash parameters allowed by the controller
-
#-----------------------------------------------------------------------------
-
1
FORM_PARAMS = [
-
:id,
-
:policy_id,
-
:asset_subtype_id,
-
:min_service_life_months,
-
:replacement_cost,
-
:cost_fy_year,
-
:replace_with_new,
-
:replace_with_leased,
-
:replace_asset_subtype_id,
-
:replacement_cost,
-
:lease_length_months,
-
-
:rehabilitation_service_month,
-
:rehabilitation_labor_cost,
-
:rehabilitation_parts_cost,
-
:extended_service_life_months,
-
-
:min_used_purchase_service_life_months
-
]
-
-
#-----------------------------------------------------------------------------
-
#
-
# Class Methods
-
#
-
#-----------------------------------------------------------------------------
-
1
def self.allowable_params
-
1
FORM_PARAMS
-
end
-
-
#-----------------------------------------------------------------------------
-
#
-
# Instance Methods
-
#
-
#-----------------------------------------------------------------------------
-
-
# Returns the total rehabilitation cost for the asset type
-
1
def total_rehabilitation_cost
-
1
rehabilitation_labor_cost.to_i + rehabilitation_parts_cost.to_i
-
end
-
-
1
def to_s
-
1
"#{asset_subtype.asset_type}: #{asset_subtype}"
-
end
-
-
1
def name
-
1
"Policy Rule #{asset_subtype}"
-
end
-
-
-
# Override setters to remove any extraneous formats from the number strings eg $, etc.
-
1
def min_service_life_months=(num)
-
114
self[:min_service_life_months] = sanitize_to_int(num)
-
end
-
1
def replacement_cost=(num)
-
112
self[:replacement_cost] = sanitize_to_int(num)
-
end
-
1
def lease_length_months=(num)
-
57
self[:lease_length_months] = sanitize_to_int(num)
-
end
-
1
def rehabilitation_service_month=(num)
-
58
self[:rehabilitation_service_month] = sanitize_to_int(num)
-
end
-
1
def rehabilitation_labor_cost=(num)
-
58
self[:rehabilitation_labor_cost] = sanitize_to_int(num)
-
end
-
1
def rehabilitation_parts_cost=(num)
-
58
self[:rehabilitation_parts_cost] = sanitize_to_int(num)
-
end
-
1
def extended_service_life_months=(num)
-
57
self[:extended_service_life_months] = sanitize_to_int(num)
-
end
-
1
def min_used_purchase_service_life_months=(num)
-
57
self[:min_used_purchase_service_life_months] = sanitize_to_int(num)
-
end
-
-
#-----------------------------------------------------------------------------
-
1
def minimum_value(attr, default = 0)
-
# This method determines the minimum value allowed for an input for a particular attribute.
-
# It allows us to set the appropriate minimum value for the inputs on the form.
-
-
3
if policy.parent.present?
-
2
parent_rule = policy.parent.policy_asset_subtype_rules.find_by(asset_subtype: self.asset_subtype)
-
2
if parent_rule.present?
-
1
parent_rule.send attr.to_s
-
else
-
1
default
-
end
-
else
-
1
default
-
end
-
end
-
-
1
def min_allowable_policy_attributes
-
76
[
-
:min_service_life_months,
-
:extended_service_life_months,
-
:min_used_purchase_service_life_months
-
]
-
end
-
-
1
def min_allowable_policy_values(subtype=nil)
-
82
subtype = self.asset_subtype if subtype.nil?
-
# This method gets the min values for child orgs that are not less than the value
-
# set by the parent org
-
82
results = Hash.new
-
-
82
if policy.present? and policy.parent.present?
-
32
attributes_to_compare = min_allowable_policy_attributes
-
-
32
parent_rule = policy.parent.policy_asset_subtype_rules.find_by(asset_subtype: subtype)
-
-
32
if parent_rule.present?
-
16
attributes_to_compare.each do |attr|
-
48
results[attr] = parent_rule.send(attr)
-
end
-
end
-
end
-
82
results
-
end
-
#-----------------------------------------------------------------------------
-
# Protected Methods
-
#-----------------------------------------------------------------------------
-
1
protected
-
-
# Set resonable defaults for a new policy
-
1
def set_defaults
-
189
self.min_service_life_months ||= 0
-
189
self.replacement_cost ||= 0
-
189
self.lease_length_months ||= 0
-
189
self.rehabilitation_service_month ||= 0
-
189
self.rehabilitation_labor_cost ||= 0
-
189
self.rehabilitation_parts_cost ||= 0
-
189
self.extended_service_life_months ||= 0
-
189
self.min_used_purchase_service_life_months ||= 0
-
189
self.cost_fy_year ||= current_planning_year_year
-
end
-
-
1
def distribute_policy
-
# distribute rule if parent policy
-
71
if self.policy.parent_id.nil? && (previous_changes.keys.map(&:to_s) & min_allowable_policy_attributes.map(&:to_s)).count > 0
-
41
subtype_rules = PolicyAssetSubtypeRule.includes(:policy).where(policies: {parent_id: self.policy_id},policy_asset_subtype_rules: {asset_subtype_id: self.asset_subtype_id})
-
41
subtype_rules.each do |subtype_rule|
-
2
parent_rules = subtype_rule.min_allowable_policy_values
-
-
8
subtype_rule.update_columns(subtype_rule.attributes.slice(*parent_rules.stringify_keys.keys).merge(parent_rules.stringify_keys){|key, oldval, newval| [oldval, newval].max})
-
-
end
-
end
-
end
-
-
# this has to be done after_commit in case there are other after_save calls that distribute other policy fields
-
1
def apply_distributed_policy
-
71
if self.policy.parent_id.nil?
-
44
Delayed::Job.enqueue PolicyAssetSubtypeRuleDistributerJob.new(PolicyAssetSubtypeRule.includes(:policy).where(policies: {parent_id: self.policy_id},policy_asset_subtype_rules: {asset_subtype_id: self.asset_subtype_id}).pluck('policy_asset_subtype_rules.id').join(',')), :priority => 0
-
end
-
end
-
-
1
def apply_policy
-
17
TransamAsset.operational.where(organization_id: self.policy.organization_id).each do |asset|
-
Rails.logger.warn "Issue applying policy on TransAM Asset #{asset}" unless asset.save
-
end
-
end
-
1
handle_asynchronously :apply_policy
-
-
#-----------------------------------------------------------------------------
-
1
private
-
#-----------------------------------------------------------------------------
-
-
1
def validate_min_allowable_policy_values
-
# This method validates that values for child orgs are not less than the value
-
# set by the parent org
-
80
return_value = true
-
-
80
min_allowable_policy_values.each do |attr, val|
-
# Make sure we don't try to test nil values. Other validations should
-
# take care of these
-
42
if self.send(attr).blank?
-
next
-
end
-
-
42
if self.send(attr) < val
-
1
errors.add(attr, "cannot be less than #{val}, which is the minimum set by #{ policy.parent.organization.short_name}'s policy")
-
1
return_value = false
-
end
-
end
-
-
80
return_value
-
end
-
-
end
-
#-------------------------------------------------------------------------------
-
#
-
# PolicyAssetTypeRule
-
#
-
# Policy rule for an asset type for an organiation
-
#
-
#-------------------------------------------------------------------------------
-
1
class PolicyAssetTypeRule < ActiveRecord::Base
-
-
# Include the numeric sanitizers mixin
-
1
include TransamNumericSanitizers
-
-
#------------------------------------------------------------------------------
-
# Callbacks
-
#------------------------------------------------------------------------------
-
1
after_initialize :set_defaults
-
-
1
after_commit :apply_policy
-
-
#-----------------------------------------------------------------------------
-
# Associations
-
#-----------------------------------------------------------------------------
-
# Every policy rule belongs to a policy, and thus an organization
-
1
belongs_to :policy
-
# Every one of these rules applies to an asset type
-
1
belongs_to :asset_type
-
# Every asset type rule has a service life calculator
-
1
belongs_to :service_life_calculation_type
-
# Every asset type rule has a replacement cost calculator
-
1
belongs_to :replacement_cost_calculation_type, :class_name => "CostCalculationType"
-
# Every asset type rule has a condition rollup calculator
-
1
belongs_to :condition_rollup_calculation_type
-
-
#-----------------------------------------------------------------------------
-
# Validations
-
#-----------------------------------------------------------------------------
-
1
validates :policy, :presence => true
-
1
validates :asset_type, :presence => true
-
1
validates :service_life_calculation_type, :presence => true
-
1
validates :replacement_cost_calculation_type, :presence => true
-
1
validates :annual_inflation_rate, :presence => true, :numericality => {:greater_than_or_equal_to => 0.01, :less_than_or_equal_to => 100}
-
1
validates :pcnt_residual_value, :presence => true, :numericality => {:only_integer => true, :greater_than_or_equal_to => 0, :less_than_or_equal_to => 100}
-
-
#-----------------------------------------------------------------------------
-
# Scopes
-
#-----------------------------------------------------------------------------
-
437
default_scope { order(:asset_type_id) }
-
-
#-----------------------------------------------------------------------------
-
# List of hash parameters allowed by the controller
-
#-----------------------------------------------------------------------------
-
1
FORM_PARAMS = [
-
:id,
-
:policy_id,
-
:asset_type_id,
-
:service_life_calculation_type_id,
-
:replacement_cost_calculation_type_id,
-
:condition_rollup_calculation_type_id,
-
:annual_inflation_rate,
-
:pcnt_residual_value,
-
:condition_rollup_weight
-
]
-
-
#------------------------------------------------------------------------------
-
#
-
# Class Methods
-
#
-
#------------------------------------------------------------------------------
-
1
def self.allowable_params
-
1
FORM_PARAMS
-
end
-
-
#------------------------------------------------------------------------------
-
#
-
# Instance Methods
-
#
-
#------------------------------------------------------------------------------
-
-
1
def to_s
-
1
"#{asset_type}"
-
end
-
-
1
def name
-
1
"Policy Rule #{asset_type}"
-
end
-
-
# Override setters to remove any extraneous formats from the number strings eg $, etc.
-
1
def annual_inflation_rate=(num)
-
90
self[:annual_inflation_rate] = sanitize_to_float(num)
-
end
-
1
def pcnt_residual_value=(num)
-
88
self[:pcnt_residual_value] = sanitize_to_int(num)
-
end
-
#------------------------------------------------------------------------------
-
# Protected Methods
-
#------------------------------------------------------------------------------
-
1
protected
-
-
# Set resonable defaults for a new policy
-
1
def set_defaults
-
187
self.annual_inflation_rate ||= 1.1
-
187
self.pcnt_residual_value ||= 0
-
187
self.condition_rollup_weight ||= 0
-
end
-
-
1
def apply_policy
-
48
TransamAsset.operational.where(organization_id: self.policy.organization_id).each do |asset|
-
Rails.logger.warn "Issue applying policy on TransAM Asset #{asset}" unless asset.save
-
end
-
end
-
1
handle_asynchronously :apply_policy
-
-
end
-
-
class PolicyDistributerProxy < Proxy
-
# General state variables
-
-
# Type of asset type to process
-
attr_accessor :policy, :apply_policies
-
-
validates :policy, :presence => true
-
-
def initialize(attrs = {})
-
super
-
attrs.each do |k, v|
-
self.send "#{k}=", v
-
end
-
end
-
end
-
1
class PriorityType < ActiveRecord::Base
-
-
# All types that are available
-
2
scope :active, -> { where(:active => true) }
-
-
1
def self.default
-
6
find_by(:is_default => true)
-
end
-
-
1
def to_s
-
2
name
-
end
-
-
end
-
#------------------------------------------------------------------------------
-
#
-
# ProcessLog
-
#
-
# Holds all processing messages for a bulk update
-
# and the DSL to create or view them
-
#
-
#------------------------------------------------------------------------------
-
class ProcessLog
-
-
attr_reader :process_log
-
-
def initialize
-
@process_log = []
-
@log_level = 1
-
end
-
-
# Adds a message to the process log
-
def add_processing_message(level, severity, text)
-
-
# See if we are bumping the level up or down
-
if @log_level < level
-
while @log_level < level
-
@process_log << "<ul>"
-
@log_level += 1
-
end
-
elsif @log_level > level
-
while @log_level > level
-
@process_log << "</ul>"
-
@log_level -= 1
-
end
-
end
-
@log_level = level
-
-
# Log Level 1 is a special case for top-level information.
-
# All other levels are subordinate to some larger piece (e.g. MileageUpdate processing)
-
if level == 1
-
@process_log << "<p class='text-#{severity}'>#{text}</p>"
-
else
-
@process_log << "<li><p class='text-#{severity}'>#{text}</p></li>"
-
end
-
end
-
-
def to_s
-
@process_log.join('')
-
end
-
-
def empty?
-
@process_log.empty?
-
end
-
end
-
1
require 'active_model'
-
-
# Base class for transient models. Provides naming and validations via ActiveModel
-
# so concrete classes can call xxx.valid? to validate the transient
-
# model and forms can process error messages.
-
1
class Proxy
-
1
include ActiveModel::Conversion
-
1
include ActiveModel::Validations
-
1
attr_reader :errors
-
-
1
def initialize(attrs = {})
-
8
@errors = ActiveModel::Errors.new(self)
-
end
-
-
# ensure that they are never stored in the database
-
1
def persist
-
@persisted = false
-
end
-
-
1
def persisted?
-
1
@persisted
-
end
-
-
end
-
1
class QueryAssetClass < ApplicationRecord
-
1
has_many :query_field_asset_classes, dependent: :destroy
-
1
has_many :query_fields, through: :query_field_asset_classes
-
-
1
validates :table_name, presence: true, uniqueness: true
-
end
-
1
class QueryAssociationClass < ApplicationRecord
-
end
-
1
class QueryCategory < ApplicationRecord
-
1
has_many :query_fields, dependent: :destroy
-
end
-
1
class QueryField < ApplicationRecord
-
1
has_many :query_field_asset_classes, dependent: :destroy
-
1
has_many :query_asset_classes, through: :query_field_asset_classes
-
1
has_many :query_filters, dependent: :destroy
-
1
belongs_to :query_association_class
-
1
belongs_to :query_category
-
-
1
scope :auto_show, -> { where(auto_show: true) }
-
1
scope :visible, -> { where(hidden: [nil, false]) }
-
1
scope :by_category_id, -> (category_id) { where(query_category_id: category_id) }
-
end
-
1
class QueryFieldAssetClass < ApplicationRecord
-
1
belongs_to :query_field
-
1
belongs_to :query_asset_class
-
end
-
1
class QueryFilter < ApplicationRecord
-
1
belongs_to :query_field
-
1
belongs_to :saved_query
-
end
-
1
class QueryParam < ActiveRecord::Base
-
-
# All types that are available
-
1
scope :active, -> { where(:active => true) }
-
-
1
def to_s
-
name
-
end
-
-
end
-
class RecentAssetEventsView < ApplicationRecord
-
# Views don't have a discoverable PK so we need to manually set it to stop complaining
-
self.primary_key = :asset_event_id
-
# Force the model to be read-only
-
include TransamReadOnlyModel
-
belongs_to :transam_asset
-
belongs_to :asset_event
-
belongs_to :asset_event_type
-
end
-
#
-
# Rehabilitation update event. This is event type is required for
-
# all implementations
-
#
-
1
class RehabilitationUpdateEvent < AssetEvent
-
-
# Callbacks
-
1
after_initialize :set_defaults
-
-
# Associations
-
1
has_many :asset_event_asset_subsystems, :foreign_key => "asset_event_id", :dependent => :destroy
-
1
accepts_nested_attributes_for :asset_event_asset_subsystems, :allow_destroy => true, :reject_if => lambda{ |attrs| attrs[:parts_cost].blank? and attrs[:labor_cost].blank? }
-
-
1
has_many :asset_subsystems, :through => :asset_event_asset_subsystems
-
-
1
validates :extended_useful_life_months, :numericality => {:only_integer => true, :greater_than_or_equal_to => 0}, allow_nil: true
-
-
#------------------------------------------------------------------------------
-
# Scopes
-
#------------------------------------------------------------------------------
-
# set the default scope
-
81
default_scope { where(:asset_event_type_id => AssetEventType.find_by_class_name(self.name).id).order(:event_date, :created_at) }
-
-
# List of hash parameters allowed by the controller
-
1
FORM_PARAMS = [
-
:total_cost,
-
:extended_useful_life_months,
-
:asset_event_asset_subsystems_attributes => [AssetEventAssetSubsystem.allowable_params]
-
]
-
-
#------------------------------------------------------------------------------
-
#
-
# Class Methods
-
#
-
#------------------------------------------------------------------------------
-
-
1
def self.allowable_params
-
1
FORM_PARAMS
-
end
-
-
#returns the asset event type for this type of event
-
1
def self.asset_event_type
-
52
AssetEventType.find_by_class_name(self.name)
-
end
-
-
#------------------------------------------------------------------------------
-
#
-
# Instance Methods
-
#
-
#------------------------------------------------------------------------------
-
-
1
def get_update
-
1
"Rehabilitation: $#{cost}: #{asset_subsystems.join(",")}"
-
end
-
-
1
def cost
-
5
if total_cost
-
total_cost
-
else
-
5
parts_cost + labor_cost # sum up the costs from subsystems
-
end
-
end
-
-
# Cost for each piece is the sum of what's spent on subsystems
-
1
def parts_cost
-
9
asset_event_asset_subsystems.map(&:parts_cost).compact.reduce(0, :+)
-
end
-
1
def labor_cost
-
9
asset_event_asset_subsystems.map(&:labor_cost).compact.reduce(0, :+)
-
end
-
-
#------------------------------------------------------------------------------
-
#
-
# Protected Methods
-
#
-
#------------------------------------------------------------------------------
-
1
protected
-
-
# Set resonable defaults for a new rehab update event
-
1
def set_defaults
-
29
super
-
29
self.extended_useful_life_months ||= 0
-
end
-
-
end
-
1
class ReplacementReasonType < ActiveRecord::Base
-
-
# All types that are available
-
1
scope :active, -> { where(:active => true) }
-
-
1
def to_s
-
4
name
-
end
-
-
end
-
1
class Report < ActiveRecord::Base
-
-
# associations
-
1
belongs_to :report_type
-
-
-
# default scope
-
#default_scope { where(:active => true) }
-
-
1
scope :active, -> { where(:active => true) }
-
1
scope :show_in_nav, -> { where(:active => true, :show_in_nav => true) }
-
1
scope :show_in_dashboard, -> { where(:active => true, :show_in_dashboard => true) }
-
-
1
def to_s
-
1
name
-
end
-
-
# Return an array of role names allowed to access this report
-
1
def role_names
-
1
roles.split(',')
-
end
-
-
end
-
1
class ReportType < ActiveRecord::Base
-
-
# All types that are available
-
2
scope :active, -> { where(:active => true) }
-
-
1
has_many :reports
-
-
1
def to_s
-
1
name
-
end
-
-
end
-
#-------------------------------------------------------------------------------
-
# Role
-
#-------------------------------------------------------------------------------
-
1
class Role < ActiveRecord::Base
-
-
#-----------------------------------------------------------------------------
-
# Behaviors
-
#-----------------------------------------------------------------------------
-
1
scopify
-
-
#-----------------------------------------------------------------------------
-
# Associations
-
#-----------------------------------------------------------------------------
-
1
has_many :users_roles
-
1
has_many :users, :through => :users_roles
-
-
1
belongs_to :resource, :polymorphic => true
-
1
belongs_to :role_parent, :class_name => 'Role'
-
-
#-----------------------------------------------------------------------------
-
# Scopes
-
#-----------------------------------------------------------------------------
-
4492
default_scope { order(:weight) }
-
# True roles
-
14
scope :roles, -> { where(privilege: false) }
-
# Privilege roles
-
16
scope :privileges, -> { where(privilege: true) }
-
-
#-----------------------------------------------------------------------------
-
# Class Methods
-
#-----------------------------------------------------------------------------
-
-
#-----------------------------------------------------------------------------
-
# Instance Methods
-
#-----------------------------------------------------------------------------
-
-
1
def to_s
-
373
name
-
end
-
-
1
def label
-
353
read_attribute(:label) || name.titleize
-
end
-
-
1
def privilege?
-
576
(privilege)
-
end
-
-
end
-
1
class RuleSet < ActiveRecord::Base
-
-
1
include TransamObjectKey
-
-
# Callbacks
-
1
after_initialize :set_defaults
-
-
1
def to_s
-
1
name
-
end
-
-
1
protected
-
-
1
def set_defaults
-
3
self.rule_set_aware = self.rule_set_aware.nil? ? true : self.rule_set_aware
-
end
-
-
end
-
#-------------------------------------------------------------------------------
-
#
-
# Saved Query
-
#
-
# Represents a search that has been persisted to the database by the user
-
#
-
#-------------------------------------------------------------------------------
-
1
class SavedQuery < ActiveRecord::Base
-
1
serialize :ordered_output_field_ids, Array
-
-
#-----------------------------------------------------------------------------
-
# Behaviors
-
#-----------------------------------------------------------------------------
-
-
# Include the object key mixin
-
1
include TransamObjectKey
-
1
attr_accessor :organization_list
-
-
#-----------------------------------------------------------------------------
-
# Callbacks
-
#-----------------------------------------------------------------------------
-
1
after_initialize :set_defaults
-
-
#-----------------------------------------------------------------------------
-
# Associations
-
#-----------------------------------------------------------------------------
-
# Each saved search belongs to a user
-
1
belongs_to :created_by_user, :class_name => "User", :foreign_key => :created_by_user_id
-
1
belongs_to :updated_by_user, :class_name => "User", :foreign_key => :updated_by_user_id
-
#belongs_to :shared_from_org, :class_name => "Organization", :foreign_key => :shared_from_org_id
-
-
1
has_and_belongs_to_many :organizations
-
-
1
has_many :saved_query_fields, dependent: :destroy
-
1
has_many :query_fields, through: :saved_query_fields
-
-
1
has_many :query_filters, dependent: :destroy
-
-
#-----------------------------------------------------------------------------
-
# Validations
-
#-----------------------------------------------------------------------------
-
#validates :created_by_user, :presence => true
-
#validates :updated_by_user, :presence => true
-
1
validates :name, :presence => true, :uniqueness => {scope: :created_by_user, message: "should be unique per user"}
-
1
validates :description, :presence => true
-
-
-
#-----------------------------------------------------------------------------
-
# Scopes
-
#-----------------------------------------------------------------------------
-
-
-
#-----------------------------------------------------------------------------
-
# Constants
-
#-----------------------------------------------------------------------------
-
-
# List of hash parameters allowed by the controller
-
1
FORM_PARAMS = [
-
:name,
-
:description,
-
:organization_ids => [],
-
:query_field_ids => [],
-
:query_filters => [:query_field_id, :value, :op]
-
]
-
-
# List of fields which can be searched using a simple text-based search
-
1
SEARCHABLE_FIELDS = [
-
:name,
-
:description
-
]
-
-
#-----------------------------------------------------------------------------
-
#
-
# Class Methods
-
#
-
#-----------------------------------------------------------------------------
-
-
1
def self.allowable_params
-
FORM_PARAMS
-
end
-
-
1
def self.searchable_fields
-
SEARCHABLE_FIELDS
-
end
-
-
#-----------------------------------------------------------------------------
-
#
-
# Instance Methods
-
#
-
#-----------------------------------------------------------------------------
-
-
1
def to_s
-
name
-
end
-
-
1
def shared?
-
!organizations.empty?
-
end
-
-
1
def parse_query_fields(query_field_ids, query_filters_data)
-
query_field_ids = query_field_ids.map(&:to_i)
-
visible_query_fields = QueryField.where(id: query_field_ids)
-
# find pair fields
-
paired_fields = QueryField.where(name: visible_query_fields.where.not(pairs_with: nil).pluck(:pairs_with).uniq)
-
self.query_fields = visible_query_fields.or(paired_fields)
-
-
# TODO: sort
-
query_field_has_pairs_hash = visible_query_fields.where.not(pairs_with: nil).pluck(:id, :pairs_with).to_h
-
paired_fields_hash = paired_fields.pluck(:name, :id).to_h
-
query_field_has_pairs_hash.each do |field_id, pair_with_field_name|
-
paired_field_id = paired_fields_hash[pair_with_field_name]
-
idx = query_field_ids.index(field_id)
-
query_field_ids.insert idx+1, paired_field_id
-
end
-
-
self.ordered_output_field_ids = query_field_ids
-
-
filters = []
-
(query_filters_data || []).each do |filter_data|
-
filters << QueryFilter.new(filter_data)
-
end
-
-
self.query_filters = filters
-
end
-
-
1
def ordered_query_fields
-
query_fields.sort_by{|f| self.ordered_output_field_ids.index(f.id)}
-
end
-
-
# Caches the rows
-
1
def data
-
@data ||= perform_query
-
end
-
-
1
def to_sql
-
relation.to_sql
-
end
-
-
1
def clone!
-
cloned_query = SavedQuery.new
-
-
# base attrs
-
cloned_query.name = self.name + " (Copy)"
-
cloned_query.description = self.description
-
cloned_query.ordered_output_field_ids = self.ordered_output_field_ids
-
-
# copy query fields
-
self.saved_query_fields.each do |f|
-
cloned_query.saved_query_fields << f.dup
-
end
-
-
# copy query filters
-
self.query_filters.each do |qf|
-
cloned_query.query_filters << qf.dup
-
end
-
-
cloned_query
-
end
-
-
#-----------------------------------------------------------------------------
-
#
-
# Protected Methods
-
#
-
#-----------------------------------------------------------------------------
-
1
protected
-
-
# Set reasonable defaults for a new saved search
-
1
def set_defaults
-
-
end
-
-
# Perform query
-
1
def perform_query
-
relation
-
end
-
-
-
#-----------------------------------------------------------------------------
-
#
-
# Private Methods
-
#
-
#-----------------------------------------------------------------------------
-
-
# Compose query relation
-
1
def relation
-
# base query relation for asset
-
base_rel = TransamAsset.where("transam_assets.organization_id": organization_list || [])
-
-
join_tables = {}
-
where_sqls = {}
-
field_pairs = {}
-
query_filters.each do |filter|
-
query_field = filter.query_field
-
# exclude organization_id as its handled above
-
next unless query_field || query_field.name == 'organization_id'
-
unless query_field.pairs_with.blank?
-
field_pairs[query_field.name] = query_field.pairs_with
-
end
-
-
where_sqls_for_one_filter = []
-
query_field.query_asset_classes.each do |asset_class|
-
asset_table_name = asset_class.table_name
-
unless join_tables.keys.include?(asset_table_name) || asset_class.transam_assets_join.blank?
-
join_tables[asset_table_name] = asset_class.transam_assets_join
-
end
-
-
query_field_name = "#{asset_table_name}.#{query_field.name}"
-
query_filter_type = query_field.filter_type
-
-
filter_value = filter.value
-
# wrap values
-
if filter.op == 'like'
-
filter_value = "'%#{filter.value}%'"
-
elsif ['date', 'text'].include?(query_filter_type)
-
filter_value = "'#{filter.value}'"
-
end
-
-
filter_op = filter.op
-
if filter_op == 'in'
-
if filter_value.blank?
-
filter_op = 'is'
-
filter_value = 'NULL'
-
else
-
filter_value = "(#{filter_value})"
-
end
-
end
-
-
#if query_filter_type == 'text'
-
#query_field_name = "lower(#{query_field_name})"
-
#filter_value = "lower(#{filter_value})"
-
#end
-
-
where_sqls_for_one_filter << "#{query_field_name} #{filter_op} #{filter_value}"
-
end
-
-
where_sqls[query_field.name] = where_sqls_for_one_filter.join(" OR ")
-
end
-
-
# deal with field_pairs: if both the main field and pairs_with field are filters, then they should be a OR sql relation
-
# e.g., manufacturer_id in (1,2) OR other_manufacturer in ('A', 'B')
-
field_pairs.each do |main_field, pairs_with|
-
main_field_sql = where_sqls[main_field]
-
pairs_with_sql = where_sqls[pairs_with]
-
next if main_field_sql.blank? || pairs_with_sql.blank?
-
where_sqls[main_field] = "(#{main_field_sql}) OR (#{pairs_with_sql})"
-
where_sqls.delete(pairs_with) # delete pairs_with sql
-
end
-
-
select_sqls = []
-
column_filters = {}
-
query_fields.each do |field|
-
unless field.column_filter.blank? || field.column_filter_value.blank?
-
if column_filters[field.column_filter]
-
column_filters[field.column_filter] << field.column_filter_value unless column_filters[field.column_filter].include?(field.column_filter_value)
-
else
-
column_filters[field.column_filter] = [field.column_filter_value]
-
end
-
end
-
-
query_field_name = field.name
-
field_association = field.query_association_class
-
if field_association
-
association_table_name = field_association.table_name
-
association_id_field_name = field_association.id_field_name
-
association_display_field_name = field_association.display_field_name
-
end
-
-
field.query_asset_classes.each do |qac|
-
asset_table_name = qac.table_name
-
table_join = qac.transam_assets_join
-
-
unless join_tables.keys.include?(asset_table_name) || table_join.blank?
-
join_tables[asset_table_name] = table_join
-
end
-
-
unless association_table_name.blank?
-
as_table_name = "#{asset_table_name}_#{association_table_name}"
-
# select value from association table
-
unless join_tables.keys.include?(as_table_name)
-
join_tables[as_table_name] = "left join #{association_table_name} as #{as_table_name} on #{as_table_name}.#{association_id_field_name} = #{asset_table_name}.#{query_field_name}"
-
end
-
select_sqls << "#{as_table_name}.#{association_display_field_name} as #{asset_table_name}_#{query_field_name}"
-
else
-
# select value directly from asset_table
-
-
output_field_name = field.display_field.blank? ? "#{asset_table_name}.#{query_field_name}" : "#{asset_table_name}.#{field.display_field}"
-
select_sqls << "#{output_field_name} as #{asset_table_name}_#{query_field_name}"
-
end
-
end
-
end
-
-
# joins
-
join_tables.each do |table_name, join_sql|
-
base_rel = base_rel.joins(join_sql)
-
end
-
-
# wheres
-
if where_sqls.any?
-
base_rel = base_rel.where(where_sqls.map{ |field_name, field_sql| "(" + field_sql + ")" }.join(" AND "))
-
end
-
-
# apply column filters
-
if column_filters.any?
-
base_rel = base_rel.where(column_filters.map{ |column_filter, column_filter_values| "#{column_filter} in (" + column_filter_values.map{|v| "'#{v}'"}.join(',') + ")" }.join(" OR "))
-
end
-
-
# selects
-
if select_sqls.any?
-
base_rel = base_rel.select("transam_assets.id", select_sqls.join(", "))
-
end
-
-
# return base query relation
-
puts base_rel.to_sql
-
base_rel
-
end
-
-
end
-
1
class SavedQueryField < ApplicationRecord
-
1
belongs_to :saved_query
-
1
belongs_to :query_field
-
end
-
#-------------------------------------------------------------------------------
-
#
-
# Saved Search
-
#
-
# Represents a search that has been persisted to the database by the user. The
-
# user can set params to show the search in the dashboard, in the nav bar,
-
# and select wether to save the seasrch results as a *snapshot* which stores
-
# the selected rows' objects keys or as a *query* which will find matching rows
-
# based on the current state of the orders
-
#
-
#-------------------------------------------------------------------------------
-
1
class SavedSearch < ActiveRecord::Base
-
-
#-----------------------------------------------------------------------------
-
# Behaviors
-
#-----------------------------------------------------------------------------
-
-
# Include the object key mixin
-
1
include TransamObjectKey
-
-
#-----------------------------------------------------------------------------
-
# Callbacks
-
#-----------------------------------------------------------------------------
-
1
after_initialize :set_defaults
-
-
#-----------------------------------------------------------------------------
-
# Associations
-
#-----------------------------------------------------------------------------
-
# Each saved search belongs to a user
-
1
belongs_to :user
-
-
1
belongs_to :search_type
-
-
1
has_and_belongs_to_many :organizations
-
-
#-----------------------------------------------------------------------------
-
# Validations
-
#-----------------------------------------------------------------------------
-
1
validates :user, :presence => true
-
1
validates :search_type, :presence => true
-
1
validates :name, :presence => true, :uniqueness => {scope: :user, message: "should be unique per user"}
-
1
validates :description, :presence => true
-
-
-
#-----------------------------------------------------------------------------
-
# Scopes
-
#-----------------------------------------------------------------------------
-
-
# set the default scope
-
2
default_scope { order("ordinal asc, created_at desc") }
-
1
validates :ordinal, :numericality => {:only_integer => true, :greater_than => 0}, :allow_nil => true
-
-
#-----------------------------------------------------------------------------
-
# Constants
-
#-----------------------------------------------------------------------------
-
-
# List of hash parameters allowed by the controller
-
1
FORM_PARAMS = [
-
:ordinal,
-
:search_type_id,
-
:name,
-
:description,
-
:organization_ids => []
-
]
-
-
# List of fields which can be searched using a simple text-based search
-
1
SEARCHABLE_FIELDS = [
-
:name,
-
:description
-
]
-
-
#-----------------------------------------------------------------------------
-
#
-
# Class Methods
-
#
-
#-----------------------------------------------------------------------------
-
-
1
def self.allowable_params
-
FORM_PARAMS
-
end
-
-
1
def self.searchable_fields
-
SEARCHABLE_FIELDS
-
end
-
-
#-----------------------------------------------------------------------------
-
#
-
# Instance Methods
-
#
-
#-----------------------------------------------------------------------------
-
-
1
def to_s
-
name
-
end
-
-
# Inflates a search proxy from the saved JSON
-
1
def search_proxy
-
if json.present?
-
h = JSON.parse(json)
-
h.except('errors', 'organization_id')
-
end
-
end
-
-
1
def shared?
-
!organizations.empty?
-
end
-
-
#-----------------------------------------------------------------------------
-
#
-
# Protected Methods
-
#
-
#-----------------------------------------------------------------------------
-
1
protected
-
-
# Set reasonable defaults for a new saved search
-
1
def set_defaults
-
-
end
-
-
end
-
#
-
# Schedule Disposition update event. This is event type is required for
-
# all implementations
-
#
-
1
class ScheduleDispositionUpdateEvent < AssetEvent
-
-
# Callbacks
-
1
after_initialize :set_defaults
-
-
# Associations
-
-
1
validates :disposition_year, :presence => true
-
-
#------------------------------------------------------------------------------
-
# Scopes
-
#------------------------------------------------------------------------------
-
# set the default scope
-
15
default_scope { where(:asset_event_type_id => AssetEventType.find_by_class_name(self.name).id).order(:event_date, :created_at) }
-
-
# List of hash parameters allowed by the controller
-
1
FORM_PARAMS = [
-
:disposition_year
-
]
-
-
#------------------------------------------------------------------------------
-
#
-
# Class Methods
-
#
-
#------------------------------------------------------------------------------
-
-
1
def self.allowable_params
-
1
FORM_PARAMS
-
end
-
-
#returns the asset event type for this type of event
-
1
def self.asset_event_type
-
9
AssetEventType.find_by_class_name(self.name)
-
end
-
-
#------------------------------------------------------------------------------
-
#
-
# Instance Methods
-
#
-
#------------------------------------------------------------------------------
-
-
# This must be overriden otherwise a stack error will occur
-
1
def get_update
-
1
"Scheduled for disposition in #{fiscal_year(disposition_year)}"
-
end
-
-
1
protected
-
-
# Set resonable defaults for a new schedule disposition update event
-
1
def set_defaults
-
6
super
-
6
self.disposition_year ||= current_planning_year_year
-
end
-
end
-
#
-
# Schedule Rehabilitation update event. This is event type is required for
-
# all implementations
-
#
-
1
class ScheduleRehabilitationUpdateEvent < AssetEvent
-
-
# Callbacks
-
1
after_initialize :set_defaults
-
-
1
validates :rebuild_year, :presence => true, :numericality => { :only_integer => true, :greater_than_or_equal_to => Date.today.year - 10 }
-
-
#------------------------------------------------------------------------------
-
# Scopes
-
#------------------------------------------------------------------------------
-
# set the default scope
-
15
default_scope { where(:asset_event_type_id => AssetEventType.find_by_class_name(self.name).id).order(:event_date, :created_at) }
-
-
# List of hash parameters allowed by the controller
-
1
FORM_PARAMS = [
-
:rebuild_year
-
]
-
-
#------------------------------------------------------------------------------
-
#
-
# Class Methods
-
#
-
#------------------------------------------------------------------------------
-
-
1
def self.allowable_params
-
3
FORM_PARAMS
-
end
-
-
#returns the asset event type for this type of event
-
1
def self.asset_event_type
-
9
AssetEventType.find_by_class_name(self.name)
-
end
-
-
#------------------------------------------------------------------------------
-
#
-
# Instance Methods
-
#
-
#------------------------------------------------------------------------------
-
-
# This must be overriden otherwise a stack error will occur
-
1
def get_update
-
1
"Scheduled for rehabilitation in #{fiscal_year(rebuild_year)}"
-
end
-
-
1
protected
-
-
# Set resonable defaults for a new condition update event
-
1
def set_defaults
-
6
super
-
6
self.rebuild_year ||= current_planning_year_year
-
6
self.asset_event_type ||= AssetEventType.find_by_class_name(self.name)
-
end
-
-
-
end
-
#
-
# Schedule Replacement update event. This is event type is required for
-
# all implementations
-
#
-
1
class ScheduleReplacementUpdateEvent < AssetEvent
-
-
# Callbacks
-
1
after_initialize :set_defaults
-
-
# Associations
-
1
belongs_to :replacement_reason_type
-
-
1
validates :replacement_year, :presence => true, :numericality => {:only_integer => true, :greater_than_or_equal_to => Date.today.year - 10}
-
1
validates :replacement_reason_type, :presence => true
-
-
#------------------------------------------------------------------------------
-
# Scopes
-
#------------------------------------------------------------------------------
-
# set the default scope
-
19
default_scope { where(:asset_event_type_id => AssetEventType.find_by_class_name(self.name).id).order(:event_date, :created_at) }
-
-
# List of hash parameters allowed by the controller
-
1
FORM_PARAMS = [
-
:replacement_reason_type_id,
-
:replacement_year
-
]
-
-
#------------------------------------------------------------------------------
-
#
-
# Class Methods
-
#
-
#------------------------------------------------------------------------------
-
-
1
def self.allowable_params
-
1
FORM_PARAMS
-
end
-
-
#returns the asset event type for this type of event
-
1
def self.asset_event_type
-
9
AssetEventType.find_by_class_name(self.name)
-
end
-
-
#------------------------------------------------------------------------------
-
#
-
# Instance Methods
-
#
-
#------------------------------------------------------------------------------
-
-
# This must be overriden otherwise a stack error will occur
-
1
def get_update
-
1
"Scheduled for replacement in #{fiscal_year(replacement_year)}. Reason: #{replacement_reason_type}."
-
end
-
-
1
protected
-
-
# Set resonable defaults for a new condition update event
-
1
def set_defaults
-
10
super
-
10
self.replacement_year ||= current_planning_year_year
-
10
self.asset_event_type ||= AssetEventType.find_by_class_name(self.name)
-
10
self.replacement_reason_type ||= ReplacementReasonType.find_by(:name => "Reached policy EUL")
-
end
-
-
end
-
1
class SearchType < ActiveRecord::Base
-
-
# All types that are available
-
1
scope :active, -> { where(:active => true) }
-
-
1
def to_s
-
name
-
end
-
-
end
-
1
class SerialNumber < ApplicationRecord
-
1
belongs_to :identifiable, polymorphic: true
-
-
1
validates :identifiable, presence: true
-
1
validates :identification, presence: true
-
-
1
def to_s
-
identification
-
end
-
end
-
1
class ServiceLifeCalculationType < ActiveRecord::Base
-
-
# All types that are available
-
1
scope :active, -> { where(:active => true) }
-
-
1
def to_s
-
1
name
-
end
-
-
end
-
1
class ServiceStatusType < ActiveRecord::Base
-
-
2
scope :active, -> { where(active: true) }
-
-
1
def self.search(text, exact = true)
-
2
if exact
-
1
x = where('name = ? OR code = ? OR description = ?', text, text, text).first
-
else
-
1
val = "%#{text}%"
-
1
x = where('name LIKE ? OR code LIKE ? OR description LIKE ?', val, val, val).first
-
end
-
2
x
-
end
-
-
1
def to_s
-
7
name
-
end
-
-
end
-
#
-
# Service Status Update Event. This is event type is required for
-
# all implementations and represents envets that change the service
-
# status of an asset: START SERVICE, SUSPEND SERVICE etc.
-
#
-
#
-
1
class ServiceStatusUpdateEvent < AssetEvent
-
-
# Callbacks
-
1
after_initialize :set_defaults
-
-
# Associations
-
-
# Service Status of the asset
-
1
belongs_to :service_status_type
-
-
-
1
validates :service_status_type_id, :presence => true
-
-
#------------------------------------------------------------------------------
-
# Scopes
-
#------------------------------------------------------------------------------
-
# set the default scope
-
34
default_scope { where(:asset_event_type_id => AssetEventType.find_by_class_name(self.name).id).order(:event_date, :created_at) }
-
-
# List of hash parameters allowed by the controller
-
1
FORM_PARAMS = [
-
:service_status_type_id
-
]
-
-
#------------------------------------------------------------------------------
-
#
-
# Class Methods
-
#
-
#------------------------------------------------------------------------------
-
-
1
def self.allowable_params
-
4
FORM_PARAMS
-
end
-
-
#returns the asset event type for this type of event
-
1
def self.asset_event_type
-
26
AssetEventType.find_by_class_name(self.name)
-
end
-
-
#------------------------------------------------------------------------------
-
#
-
# Instance Methods
-
#
-
#------------------------------------------------------------------------------
-
-
1
def get_update
-
1
"Service status changed to #{service_status_type}" unless service_status_type.nil?
-
end
-
-
# Set resonable defaults for a new condition update event
-
1
def set_defaults
-
10
super
-
10
self.service_status_type ||= transam_asset.service_status_updates.last.try(:service_status_type) if transam_asset
-
10
self.asset_event_type ||= AssetEventType.find_by_class_name(self.name)
-
end
-
-
end
-
#------------------------------------------------------------------------------
-
#
-
# SystemConfig
-
#
-
# This is a singleton that is used to set default options for TransAM applications
-
# and is used to store and access important configuration information. This
-
# class is immutable and cannot be updated at run time
-
#
-
# The correct way to use this class is to refer to the instance
-
#
-
# foo = SystemConfig.instance.foo
-
#
-
#------------------------------------------------------------------------------
-
1
class SystemConfig < ActiveRecord::Base
-
-
1
has_paper_trail on: [:update], only: [:fy_year]
-
-
#------------------------------------------------------------------------------
-
# Validations
-
#------------------------------------------------------------------------------
-
-
# Validations on core attributes
-
1
validates :customer_id, :presence => true, :uniqueness => true
-
1
validates :start_of_fiscal_year, :presence => true
-
1
validates :map_tile_provider, :presence => true
-
1
validates :srid, :presence => true
-
1
validates :min_lat, :presence => true
-
1
validates :min_lon, :presence => true
-
1
validates :max_lat, :presence => true
-
1
validates :max_lon, :presence => true
-
1
validates :search_radius, :presence => true
-
1
validates :search_units, :presence => true
-
1
validates :geocoder_components, :presence => true
-
1
validates :geocoder_region, :presence => true
-
1
validates :num_forecasting_years, :presence => true
-
1
validates :num_reporting_years, :presence => true
-
1
validates :max_rows_returned, :presence => true
-
-
#------------------------------------------------------------------------------
-
#
-
# Class Methods
-
#
-
#------------------------------------------------------------------------------
-
1
def self.instance
-
# there will be only one row, and its ID must be '1'
-
2437
find_by(id: 1)
-
end
-
-
1
def self.allowable_params
-
[:fy_year]
-
end
-
-
1
def self.formatted_version(version)
-
{
-
datetime: version.created_at,
-
event: "System Rollover", # currently only versioning FY rollovers
-
event_type: "Update: Rollover method changed to #{version.changeset['fy_year'][1].blank? ? 'automatic' : 'manual'}",
-
comments: version.changeset['fy_year'][1].blank? ? "" : "Fiscal Year set to #{fiscal_year(version.changeset['fy_year'][1])}.",
-
user: version.actor
-
}
-
end
-
-
# can't use FiscalYear mixin because database name matches mixin method name so copy custom one here
-
# Returns the calendar year formatted as a FY string
-
1
def self.fiscal_year(year)
-
-
# some controllers might have a special formatter instead of the default one to use the FY string
-
# eventually default might be a SystemConfig.instance attribute as well but for now hard-coded
-
-
if defined? params
-
klass = params[:controller].classify
-
elsif self.class.to_s.include? 'Controller'
-
klass = self.class.to_s[0..-('Controller'.length+1)]
-
else
-
klass = self.class.to_s
-
end
-
-
formatter = SystemConfig.instance.special_fiscal_year_formatters[klass]
-
formatter = SystemConfig.instance.default_fiscal_year_formatter if formatter.nil?
-
-
if formatter == 'start_year'
-
"#{year}"
-
elsif formatter == 'end_year'
-
"#{year+1}"
-
else
-
yr = year - (year < 2000 ? 1900 : 2000)
-
first = "%.2d" % yr
-
if yr == 99 # when yr == 99, yr + 1 would be 100, which causes: "FY 99-100"
-
next_yr = 00
-
else
-
next_yr = (yr + 1)
-
end
-
last = "%.2d" % next_yr
-
"FY #{first}-#{last}"
-
end
-
end
-
-
-
# set default widgets and the column they are in. these can be customized at the app level
-
# 0 is all columns or you set the column width in the widget
-
1
def self.dashboard_widgets
-
return Rails.application.config.dashboard_widgets if Rails.application.config.try(:dashboard_widgets)
-
-
widgets = []
-
SystemConfig.transam_module_names.each do |mod|
-
view_component = "#{mod}_widget"
-
widgets << [view_component, 2]
-
end
-
widgets += [
-
['assets_widget', 1],
-
#['activities_widget', 1],
-
['queues', 1],
-
['users_widget', 1],
-
['notices_widget', 3],
-
['search_widget', 3],
-
['message_queues', 3],
-
['task_queues', 3],
-
['asset_events_widget', 0]
-
]
-
-
return widgets
-
-
end
-
-
-
#
-
# Queries the gemspec to see if the transam extension has been loaded.
-
# examples:
-
# if SystemConfig.transam_engine_loaded? 'transit'
-
# SystemConfig.transam_engine_loaded? :cpt
-
# if SystemConfig.transam_engine_loaded? ('core', '>= 0.4.0')
-
#
-
# By convention all transam engines are named 'transam_xxx'. The name
-
# of the engine can be passsed as a string 'core' or symbol :transit
-
#
-
1
def self.transam_module_loaded? (engine_name, version = nil)
-
2
if version.blank?
-
1
Gem::Specification::find_all_by_name("transam_#{engine_name.to_s}").any?
-
else
-
1
Gem::Specification::find_all_by_name("transam_#{engine_name.to_s}", version).any?
-
end
-
end
-
-
#
-
# Queries the gemspec and returns an array of transam modules that have been loaded.
-
# The array is ordered by the load order that should be specified in the gemspec.
-
#
-
# Load orders are sorted lowest to highest, ties broken abitarily.
-
# To specifiy the load order in a module use the following in the gemspec
-
#
-
# s.metadata = { "load_order" => "10" }
-
#
-
# which will denote a load order of 10. Core is specified as load order 1
-
# as it should always be loaded first.
-
#
-
1
def self.transam_modules
-
12
a = []
-
12
Gem::Specification::each do |gem|
-
2556
if gem.full_name.start_with? 'transam_'
-
12
a << [gem, gem.metadata['load_order'].to_i]
-
end
-
end
-
12
a.sort! { |a,b| a[1] <=> b[1] }
-
12
modules = []
-
12
a.each do |gemspec|
-
12
modules << gemspec[0]
-
end
-
12
modules
-
end
-
#
-
# Returns an array of module names
-
#
-
1
def self.transam_module_names
-
11
a = []
-
11
self.transam_modules.each do |gem|
-
11
a << gem.name.split('_').last
-
end
-
11
a
-
end
-
-
#------------------------------------------------------------------------------
-
#
-
# Instance Methods
-
#
-
#------------------------------------------------------------------------------
-
-
# Returns the default state code. If not defined a nil is returned
-
1
def default_state_code
-
# For now get this from the geocoder
-
1619
str = geocoder_components
-
1619
if str.include? "administrative_area"
-
1619
state_code = str.split("|").first.split(":").last
-
end
-
1619
state_code
-
end
-
-
# Returns the first day of the TransAM epoch -- the earliest date which TransAM
-
# assumes are valid for assets
-
1
def epoch
-
5
Rails.application.config.epoch
-
end
-
-
# The max number of rows to be returned from a query
-
1
def max_rows_returned
-
37
Rails.application.config.max_rows_returned
-
end
-
-
-
1
def geocoder_bounds
-
1
[[min_lat, min_lon], [max_lat, max_lon]]
-
end
-
1
def map_bounds
-
1
[[min_lat, min_lon], [max_lat, max_lon]]
-
end
-
-
1
def special_fiscal_year_formatters
-
205
Rails.application.config.try(:special_fiscal_year_formatters) || Hash.new
-
end
-
-
#------------------------------------------------------------------------------
-
#
-
# Private Methods
-
#
-
#------------------------------------------------------------------------------
-
1
private
-
-
end
-
# store all the mixins injected to base models thru added engines
-
-
1
class SystemConfigExtension < ApplicationRecord
-
-
1
scope :active, -> { where(:active => true) }
-
-
end
-
#------------------------------------------------------------------------------
-
#
-
# Task
-
#
-
# A task that has been associated with another class such as a Task etc. This is a
-
# polymorphic class that can store comments against any class that includes a
-
# commentable association
-
#
-
# To use this class as an association with another class include the following line into
-
# the model
-
#
-
# has_many :tasks, :as => :taskable, :dependent => :destroy
-
#
-
#------------------------------------------------------------------------------
-
1
class Task < ActiveRecord::Base
-
-
# Include the object key mixin
-
1
include TransamObjectKey
-
-
# Include the Workflow module
-
1
include TransamWorkflow
-
-
#------------------------------------------------------------------------------
-
# Callbacks
-
#------------------------------------------------------------------------------
-
1
after_initialize :set_defaults
-
-
#------------------------------------------------------------------------------
-
# Associations
-
#------------------------------------------------------------------------------
-
1
belongs_to :taskable, :polymorphic => true
-
-
# Every task is created by a user
-
5
belongs_to :user, -> { unscope(where: :active) }
-
-
# Every task is owned by an organization. This is the
-
# organization that the task has been assigned to
-
1
belongs_to :organization
-
-
# Every task can be assigned to a user. This can be null
-
# in which case the task will be available for everyone
-
# in the :organization to take on
-
1
belongs_to :assigned_to_user, :class_name => "User", :foreign_key => "assigned_to_user_id"
-
-
# Every task is assigned a priority
-
1
belongs_to :priority_type
-
-
# Each task can have notes associated with it. Comments are destroyed when the task is destroyed
-
1
has_many :comments, :as => :commentable, :dependent => :destroy
-
-
#------------------------------------------------------------------------------
-
# Validations
-
#------------------------------------------------------------------------------
-
1
validates :user, :presence => true
-
1
validates :priority_type, :presence => true
-
1
validates :organization, :presence => true
-
1
validates :subject, :presence => true
-
1
validates :body, :presence => true
-
1
validates :complete_by, :presence => true
-
-
# List of hash parameters allowed by the controller
-
1
FORM_PARAMS = [
-
:user_id,
-
:organization_id,
-
:priority_type_id,
-
:assigned_to_user_id,
-
:subject,
-
:state,
-
:body,
-
:send_reminder,
-
:complete_by
-
]
-
-
#------------------------------------------------------------------------------
-
# Scopes
-
#------------------------------------------------------------------------------
-
116
default_scope { order('complete_by') }
-
-
# tasks which are currently active based on the workflow
-
1
scope :active, -> { where("state IN (?)", Task.active_states) }
-
-
#------------------------------------------------------------------------------
-
#
-
# State Machine
-
#
-
# Used to track the state of a task order through the completion process
-
#
-
#------------------------------------------------------------------------------
-
1
state_machine :state, :initial => :new do
-
-
#-------------------------------
-
# List of allowable states
-
#-------------------------------
-
-
# initial state. All tasks are created in this state
-
1
state :new
-
-
# state used to signify it has been started but not completed
-
1
state :started
-
-
# state used to signify it has been completed
-
1
state :completed
-
-
# state used to signify that work has been halted pending input
-
1
state :halted
-
-
# state used to indicate the task has been cancelled
-
1
state :cancelled
-
-
#---------------------------------------------------------------------------
-
# List of allowable events. Events transition a task from one state to another
-
#---------------------------------------------------------------------------
-
-
# Retract the task from the shop. This is a terminal transition
-
1
event :cancel do
-
1
transition [:new, :started, :halted] => :cancelled
-
end
-
-
# start a task
-
1
event :start do
-
1
transition :new => :started
-
end
-
-
# re-start a task
-
1
event :re_start do
-
1
transition :halted => :started
-
end
-
-
# Mark a task as being complete
-
1
event :complete do
-
1
transition [:new, :started, :halted] => :completed
-
end
-
-
# The workorder has been started
-
1
event :halt do
-
1
transition [:new, :started] => :halted
-
end
-
-
# Callbacks
-
1
before_transition do |task, transition|
-
1
Rails.logger.debug "Transitioning #{task.name} from #{transition.from_name} to #{transition.to_name} using #{transition.event}"
-
end
-
end
-
-
#------------------------------------------------------------------------------
-
#
-
# Class Methods
-
#
-
#------------------------------------------------------------------------------
-
-
1
def self.allowable_params
-
3
FORM_PARAMS
-
end
-
-
1
def self.active_states
-
10
["new", "started", "halted"]
-
end
-
-
1
def self.terminal_states
-
2
["cancelled", "completed"]
-
end
-
-
#------------------------------------------------------------------------------
-
#
-
# Instance Methods
-
#
-
#------------------------------------------------------------------------------
-
-
1
def name
-
2
subject
-
end
-
-
1
def taskable_path
-
4
return false if self.taskable.nil?
-
-
4
path = eval("Rails.application.routes.url_helpers.#{self.taskable.class.base_class.name.underscore}_path(id: '#{self.taskable.object_key}')")
-
4
begin
-
4
Rails.application.routes.recognize_path(path)
-
rescue
-
return false
-
end
-
-
4
path
-
end
-
-
#------------------------------------------------------------------------------
-
#
-
# Private Methods
-
#
-
#------------------------------------------------------------------------------
-
1
private
-
-
# Set resonable defaults for a new asset
-
1
def set_defaults
-
82
self.state ||= :new
-
82
self.send_reminder = self.send_reminder.nil? ? true : self.send_reminder
-
82
self.complete_by ||= Date.today + 1.week
-
end
-
-
end
-
class TemplateProxy < Proxy
-
-
# General state variables
-
-
# Type of template to generate
-
attr_accessor :file_content_type_id
-
-
# Organization selected -- blank if the user only has a single org
-
attr_accessor :organization_id
-
-
# Type of asset to process
-
attr_accessor :asset_class_name
-
attr_accessor :asset_seed_class_id
-
-
# Basic validations. Just checking that the form is complete
-
validates :file_content_type_id, :presence => true
-
validates :asset_seed_class_id, :presence => true
-
-
def initialize(attrs = {})
-
super
-
attrs.each do |k, v|
-
self.send "#{k}=", v
-
end
-
-
end
-
-
end
-
1
class TransamAsset < TransamAssetRecord
-
-
1
DEFAULT_OTHER_ID = -1
-
-
1
include TransamObjectKey
-
1
include FiscalYear
-
-
1
actable as: :transam_assetible
-
-
1
before_validation :cleanup_others
-
-
1
belongs_to :organization
-
1
belongs_to :asset_subtype
-
1
belongs_to :manufacturer
-
1
belongs_to :manufacturer_model
-
1
belongs_to :vendor
-
-
# an upload can be added by bulk updates - new inventory
-
1
belongs_to :upload
-
-
1
has_and_belongs_to_many :asset_groups, join_table: :asset_groups_assets, foreign_key: :transam_asset_id
-
-
# Each asset can have 0 or more dependents (parent-child relationships)
-
1
has_many :dependents, :class_name => 'TransamAsset', :foreign_key => :parent_id, :dependent => :nullify, :inverse_of => :parent
-
1
accepts_nested_attributes_for :dependents, :reject_if => :all_blank, :allow_destroy => true
-
-
# Facilities can have many vehicles stored on their premises
-
1
has_many :occupants, :class_name => 'TransamAsset', :foreign_key => :location_id, :dependent => :nullify
-
-
1
belongs_to :parent, class_name: 'TransamAsset', foreign_key: :parent_id
-
1
belongs_to :location, class_name: 'TransamAsset', foreign_key: :location_id
-
-
1
has_many :serial_numbers, as: :identifiable, inverse_of: :identifiable, dependent: :destroy
-
1
accepts_nested_attributes_for :serial_numbers
-
-
1
has_many :asset_events, :foreign_key => :base_transam_asset_id, :dependent => :destroy
-
-
# each asset has zero or more condition updates
-
3
has_many :condition_updates, -> {where :asset_event_type_id => ConditionUpdateEvent.asset_event_type.id }, :class_name => "ConditionUpdateEvent", :as => :transam_asset
-
1
accepts_nested_attributes_for :condition_updates, :reject_if => Proc.new{|ae| ae['assessed_rating'].blank? }, :allow_destroy => true
-
-
# each asset has zero or more service status updates
-
3
has_many :service_status_updates, -> {where :asset_event_type_id => ServiceStatusUpdateEvent.asset_event_type.id }, :class_name => "ServiceStatusUpdateEvent", :as => :transam_asset
-
1
accepts_nested_attributes_for :service_status_updates, :reject_if => Proc.new{|ae| ae['service_status_type_id'].blank? }, :allow_destroy => true
-
-
# each asset has zero or more location updates.
-
1
has_many :location_updates, -> {where :asset_event_type_id => LocationUpdateEvent.asset_event_type.id }, :class_name => "LocationUpdateEvent", :as => :transam_asset
-
1
accepts_nested_attributes_for :location_updates, :reject_if => Proc.new{|ae| ae['parent_key'].blank? }, :allow_destroy => true
-
-
# Each asset has zero or more images. Images are deleted when the asset is deleted
-
1
has_many :images, :as => :imagable, :dependent => :destroy
-
-
# Each asset has zero or more documents. Documents are deleted when the asset is deleted
-
1
has_many :documents, :as => :documentable, :dependent => :destroy
-
-
# Each asset has zero or more comments. Documents are deleted when the asset is deleted
-
1
has_many :comments, :as => :commentable, :dependent => :destroy
-
-
# Each asset has zero or more tasks. Tasks are deleted when the asset is deleted
-
1
has_many :tasks, :as => :taskable, :dependent => :destroy
-
-
#-----------------------------------------------------------------------------
-
# Validations
-
#-----------------------------------------------------------------------------
-
-
1
validates :asset_subtype_id, presence: true
-
1
validates :organization_id, presence: true
-
1
validates :asset_tag, presence: true, uniqueness: {scope: :organization_id }
-
1
validates :purchase_cost, presence: true
-
1
validates :purchase_cost, numericality: { greater_than_or_equal_to: 0 }
-
1
validates :purchased_new, inclusion: { in: [ true, false ] }
-
1
validates :purchase_date, presence: true #temporarily force in case used in other places but eventually will not be required
-
1
validates :in_service_date, presence: true
-
-
1
validate :object_key_is_not_asset_tag
-
-
#validates :quantity, numericality: { greater_than: 0 }
-
#validates :quantity, presence: true
-
#validates :quantity_units, presence: true
-
-
#-----------------------------------------------------------------------------
-
# Scopes
-
#-----------------------------------------------------------------------------
-
-
# operational is used in a lot of places but is actually related to ReplaceableAsset (for not disposed or not in transfer)
-
# therefore stub this out so always exists so no errors
-
1
scope :operational, -> { all }
-
-
-
1
FORM_PARAMS = [
-
:organization_id,
-
:asset_subtype_id,
-
:asset_tag,
-
:external_id,
-
:description,
-
:manufacturer_id,
-
:other_manufacturer,
-
:manufacturer_model_id,
-
:other_manufacturer_model,
-
:manufacture_year,
-
:purchase_cost,
-
:purchase_date,
-
:purchased_new,
-
:in_service_date,
-
:vendor_id,
-
:other_vendor,
-
:parent_id,
-
:quantity,
-
:quantity_unit,
-
{condition_updates_attributes: ConditionUpdateEvent.allowable_params},
-
{service_status_updates_attributes: ServiceStatusUpdateEvent.allowable_params},
-
{location_updates_attributes: LocationUpdateEvent.allowable_params}
-
]
-
-
1
CLEANSABLE_FIELDS = [
-
'object_key',
-
'asset_tag',
-
'external_id',
-
]
-
-
1
SEARCHABLE_FIELDS = [
-
:object_key,
-
:asset_tag,
-
:external_id,
-
:description,
-
:manufacturer_model,
-
:title_number
-
]
-
-
1
callable_by_submodel def self.asset_seed_class_name
-
'AssetType'
-
end
-
-
-
# Factory method to return a strongly typed subclass of a new asset
-
# based on the asset_base_class_name
-
1
def self.new_asset(asset_seed_class_name, params={})
-
-
begin
-
asset_class_name = asset_seed_class_name.class_name(opts: params)
-
rescue ArgumentError => e
-
asset_class_name = asset_seed_class_name.class_name
-
end
-
-
asset = asset_class_name.constantize.new
-
-
if asset.respond_to? "#{asset_seed_class_name.class.to_s.foreign_key}="
-
asset.send("#{asset_seed_class_name.class.to_s.foreign_key}=",asset_seed_class_name.id)
-
end
-
-
return asset
-
-
end
-
-
1
def self.very_specific
-
klass = self.all
-
assoc = klass.column_names.select{|col| col.end_with? 'ible_type'}.first
-
assoc_arr = Hash.new
-
assoc_arr[assoc] = nil
-
t = klass.distinct.where.not(assoc_arr).pluck(assoc)
-
-
while t.count == 1 && assoc.present?
-
id_col = assoc[0..-6] + '_id'
-
klass = t.first.constantize.where(id: klass.pluck(id_col))
-
assoc = klass.column_names.select{|col| col.end_with? 'ible_type'}.first
-
if assoc.present?
-
assoc_arr = Hash.new
-
assoc_arr[assoc] = nil
-
t = klass.distinct.where.not(assoc_arr).pluck(assoc)
-
end
-
end
-
-
return klass
-
-
end
-
-
# mirror method on Asset to get typed version
-
1
def self.get_typed_asset(asset)
-
15
if asset
-
15
if asset.very_specific
-
asset = asset.very_specific
-
-
seed_assoc = asset.class.asset_seed_class_name.underscore
-
begin
-
if asset.class.to_s != asset.send(seed_assoc).class_name(assets: asset)
-
asset = asset.send(seed_assoc).class_name(assets: asset).constantize.find_by(object_key: asset.object_key)
-
end
-
rescue ArgumentError => e
-
if asset.class.to_s != asset.send(seed_assoc).class_name
-
asset = asset.send(seed_assoc).class_name.constantize.find_by(object_key: asset.object_key)
-
end
-
end
-
end
-
-
15
asset
-
end
-
end
-
-
1
def very_specific
-
15
a = self.specific
-
-
15
while a.try(:specific).present? && a.specific != a
-
a = a.specific
-
end
-
-
15
return a
-
end
-
-
1
def cleansable_fields
-
arr = CLEANSABLE_FIELDS.dup
-
a = self.specific
-
-
while a.try(:specific).present? && a.specific != a
-
arr << a.class::CLEANSABLE_FIELDS.dup
-
a = a.specific
-
end
-
-
arr << a.class::CLEANSABLE_FIELDS.dup
-
-
SystemConfigExtension.where(active: true, class_name: 'TransamAsset').pluck(:extension_name).each do |ext_name|
-
if ext_name.constantize::ClassMethods.try(:cleansable_fields)
-
arr << ext_name.constantize::ClassMethods.cleansable_fields
-
end
-
end
-
-
return arr.flatten
-
end
-
-
1
def searchable_fields
-
typed_self = TransamAsset.get_typed_asset(self)
-
-
arr = (defined? typed_self.class::SEARCHABLE_FIELDS) ? typed_self.class::SEARCHABLE_FIELDS.dup : []
-
-
-
if typed_self.class.superclass.name != "TransamAssetRecord"
-
arr << typed_self.class.superclass::SEARCHABLE_FIELDS if defined? typed_self.class.superclass::SEARCHABLE_FIELDS
-
end
-
-
a = typed_self.class.try(:acting_as_model)
-
while a.present?
-
arr << a::SEARCHABLE_FIELDS.dup if defined? a::SEARCHABLE_FIELDS
-
a = a.try(:acting_as_model)
-
end
-
-
return arr.flatten
-
end
-
-
1
def event_classes
-
typed_asset = TransamAsset.get_typed_asset(self)
-
-
a = []
-
# Use reflection to return the list of has many associatiopns and filter those which are
-
# events
-
typed_asset.class.reflect_on_all_associations(:has_many).each do |assoc|
-
a << assoc.klass if assoc.class_name.end_with? 'UpdateEvent'
-
end
-
-
if typed_asset.class.superclass.name != "TransamAssetRecord"
-
klass = very_specific.class.superclass
-
klass.reflect_on_all_associations(:has_many).each do |assoc|
-
a << assoc.klass if assoc.class_name.end_with? 'UpdateEvent'
-
end
-
end
-
-
a.uniq
-
end
-
-
# def asset_events(unscoped=false)
-
# typed_asset = TransamAsset.get_typed_asset(self)
-
#
-
# events = []
-
# event_classes.each do |e|
-
# assoc_name = e.name.gsub('Event', '').underscore.pluralize
-
# assoc_name = 'early_disposition_requests' if assoc_name == 'early_disposition_request_updates'
-
# events << typed_asset.send(assoc_name).ids
-
# end
-
# if unscoped
-
# AssetEvent.unscoped.where(id: events.flatten)
-
# else
-
# AssetEvent.where(id: events.flatten)
-
# end
-
#
-
# end
-
-
# returns the list of events associated with this asset ordered by date, newest first
-
1
def history
-
asset_events.reorder(event_date: :desc, updated_at: :desc)
-
end
-
-
-
# Instantiate an asset event of the appropriate type.
-
1
def build_typed_event(asset_event_type_class)
-
-
unless self.event_classes.include? asset_event_type_class
-
raise ArgumentError, 'Invalid Asset Event Type'
-
end
-
-
typed_asset = TransamAsset.get_typed_asset(self)
-
-
if typed_asset.class.superclass.name == "TransamAssetRecord"
-
assocs = typed_asset.class.reflect_on_all_associations(:has_many)
-
else
-
assocs = typed_asset.class.reflect_on_all_associations(:has_many).select{|x| x.class_name == asset_event_type_class.to_s} + typed_asset.class.superclass.reflect_on_all_associations(:has_many)
-
end
-
-
idx = 0
-
assoc = nil
-
while assoc.nil? && idx < assocs.length
-
assoc = assocs[idx].name if assocs[idx].class_name == asset_event_type_class.to_s
-
idx += 1
-
end
-
-
typed_asset.send(assoc).build
-
end
-
-
1
def asset_type_id
-
10
asset_subtype.asset_type_id
-
end
-
-
1
def asset_type
-
8
asset_subtype.asset_type
-
end
-
-
1
def parent_name
-
parent.to_s unless parent.nil?
-
end
-
-
1
def parent_key=(object_key)
-
self.parent = TransamAsset.find_by_object_key(object_key)
-
end
-
1
def parent_key
-
parent.object_key if parent
-
end
-
-
1
def location_name
-
location.to_s unless location.nil?
-
end
-
-
1
def location_key=(object_key)
-
self.location = TransamAsset.find_by_object_key(object_key)
-
end
-
1
def location_key
-
location.object_key if location
-
end
-
-
1
def cost
-
purchase_cost
-
end
-
-
# returns the number of years since the asset was placed in service.
-
1
def age(on_date=Date.today)
-
age_in_years = if in_service_date.nil?
-
0
-
else
-
((on_date.year * 12 + on_date.month) - (in_service_date.year * 12 + in_service_date.month))/12.0
-
end
-
[(age_in_years).floor, 0].max
-
end
-
-
1
def service_status_type
-
2
if try(:disposed?)
-
ServiceStatusType.find_by(name: 'Disposed')
-
else
-
2
ServiceStatusType.find_by(id: service_status_updates.last.try(:service_status_type_id))
-
end
-
end
-
-
-
-
1
def reported_condition_date
-
# if dependents.count > 0
-
# dependents.order(:reported_condition_date).pluck(:reported_condition_date).last
-
# else
-
# condition_updates.last.try(:event_date)
-
# end
-
condition_updates.last.try(:event_date)
-
end
-
1
def reported_condition_rating
-
# if dependents.count > 0
-
# policy_analyzer.get_condition_rollup_calculation_type.class_name.constantize.new.calculate(self)
-
# else
-
# condition_updates.last.try(:assessed_rating)
-
# end
-
1
condition_updates.last.try(:assessed_rating)
-
end
-
1
def reported_condition_type
-
ConditionType.from_rating(reported_condition_rating)
-
end
-
-
# in single table inheritance we could always pull fuel_type_id cause it would just be nil on assets that didn't have a fuel type
-
# we often wrote logic on that assumption so therefore this method ensures that there is always a fuel type id even if it doesn't exist in the database table now that we've moved to class table inheritance
-
# a very specific class would respond to this if it exists in the database table
-
# if it doesn't, it will try what it acts_as and will eventually hit this method
-
1
def fuel_type_id
-
nil
-
end
-
-
-
1
private
-
-
1
def object_key_is_not_asset_tag
-
3
unless self.asset_tag.nil? || self.object_key.nil?
-
3
if self.asset_tag == self.object_key
-
@errors.add(:asset_tag, "should not be the same as the object key")
-
end
-
end
-
end
-
-
1
def cleanup_others
-
# other_manufacturer only has value when type is one of Other types
-
3
if self.changes.include?("manufacturer_id") && self.other_manufacturer.present?
-
self.other_manufacturer = nil unless Manufacturer.where(code: 'ZZZ').pluck(:id).include?(self.manufacturer_id)
-
end
-
-
# other_manufacturer_model only has value when model type is one of Other types
-
3
if self.changes.include?("manufacturer_model_id") && self.other_manufacturer_model.present?
-
self.other_manufacturer_model = nil unless ManufacturerModel.where(name: 'Other').pluck(:id).include?(self.manufacturer_model_id)
-
end
-
-
3
if self.changes.include?("vendor_id") && self.other_vendor.present?
-
self.other_vendor = nil unless self.vendor_id == DEFAULT_OTHER_ID
-
end
-
-
3
if self.changes.include?("operator_id") && self.other_operator.present?
-
self.other_operator = nil unless self.operator_id == DEFAULT_OTHER_ID
-
end
-
-
3
if self.changes.include?("title_ownership_organization_id") && self.other_title_ownership_organization.present?
-
self.other_titel_ownership_organization = nil unless self.title_ownership_organization_id == DEFAULT_OTHER_ID
-
end
-
-
3
if self.changes.include?("lienholder_id") && self.other_lienholder.present?
-
self.other_lienholder = nil unless self.lienholder_id == DEFAULT_OTHER_ID
-
end
-
end
-
end
-
#-------------------------------------------------------------------------------
-
# InspectionProxy
-
#
-
# Proxy class for gathering inspection search parameters
-
#
-
#-------------------------------------------------------------------------------
-
class TransamWorkflowModelProxy < Proxy
-
-
#-----------------------------------------------------------------------------
-
# Attributes
-
#-----------------------------------------------------------------------------
-
-
attr_accessor :include_updates
-
attr_accessor :event_name
-
attr_accessor :global_ids
-
attr_accessor :model_objs
-
-
#-----------------------------------------------------------------------------
-
# Validations
-
#-----------------------------------------------------------------------------
-
-
#-----------------------------------------------------------------------------
-
# Constants
-
#-----------------------------------------------------------------------------
-
-
#-----------------------------------------------------------------------------
-
#
-
# Class Methods
-
#
-
#-----------------------------------------------------------------------------
-
-
def self.allowable_params
-
[
-
:include_updates,
-
:event_name,
-
:global_ids => []
-
]
-
end
-
-
#-----------------------------------------------------------------------------
-
#
-
# Instance Methods
-
#
-
#-----------------------------------------------------------------------------
-
-
#-----------------------------------------------------------------------------
-
# Returns the search proxy as hash as if a form had been submitted
-
#-----------------------------------------------------------------------------
-
def to_h
-
h = {}
-
a = {}
-
FORM_PARAMS.each do |param|
-
a[param] = self.try(param) unless self.try(param).blank?
-
end
-
-
h[:transam_workflow_model_proxy] = a
-
h.with_indifferent_access
-
end
-
-
def class_name
-
model_objs.first.class.to_s
-
end
-
-
#-----------------------------------------------------------------------------
-
#
-
# Protected Methods
-
#
-
#-----------------------------------------------------------------------------
-
protected
-
-
def initialize(attrs = {})
-
super
-
attrs.each do |k, v|
-
self.send "#{k}=", v
-
end
-
-
self.model_objs = []
-
self.global_ids.each do |global_id|
-
model_objs << GlobalID::Locator.locate(global_id)
-
end
-
-
end
-
-
end
-
#
-
# Simple lookup class for distance metrics. Maybe used to add different
-
# measurement units as needed.
-
#
-
# This implementation wraps Unitwise but always extend this model rather
-
# than using Unitwise directly
-
#
-
#
-
1
class Uom
-
-
# Other units
-
1
UNIT = 'unit'
-
-
# Pre-defined weight quantities
-
1
KILOGRAM = 'kilogram'
-
1
POUND = "pound"
-
1
SHORT_TON = 'short ton'
-
#TON = SHORT_TON
-
1
TONNE = "tonne"
-
-
# Pre-defined area measurements
-
1
SQUARE_FOOT = "square foot"
-
1
SQUARE_YARD = "square yard"
-
1
SQUARE_METER = "(meter)2"
-
1
SQUARE_MILE = "square mile"
-
1
ACRE = "acre"
-
-
# Predefined volumes
-
1
LITER = "liter"
-
1
GALLON = "gallon"
-
-
# Pre-defined distance metrics that can be used to define linear distances
-
1
INCH = 'inch'
-
1
FEET = 'foot'
-
1
YARD = 'yard'
-
1
MILE = 'mile'
-
1
METER = 'meter'
-
1
KILOMETER = 'kilometer'
-
-
# predefined weight over distance
-
1
POUND_YARD = 'lb/yd'
-
1
POUND_INCH = 'lb/in'
-
1
CUBIC_YARD_MILE = 'cu yd/mi'
-
-
-
1
AREA_UNITS = [SQUARE_FOOT, SQUARE_YARD, SQUARE_METER, SQUARE_MILE, ACRE]
-
1
DISTANCE_UNITS = [INCH, FEET, YARD, MILE, METER, KILOMETER]
-
1
VOLUME_UNITS = [LITER, GALLON]
-
1
WEIGHT_UNITS = [KILOGRAM, POUND, TONNE, SHORT_TON]
-
1
WEIGHT_DISTANCE_UNITS = [POUND_YARD, POUND_INCH, CUBIC_YARD_MILE]
-
1
OTHER_UNITS = [UNIT]
-
-
1
SI_UNITS = [SQUARE_METER, METER, KILOMETER, LITER, KILOGRAM, TONNE]
-
1
USC_UNITS = [SQUARE_FOOT, SQUARE_YARD, SQUARE_MILE, ACRE, INCH, FEET, YARD, MILE, GALLON, POUND, SHORT_TON]
-
-
# Returns an array of units
-
1
def self.units
-
1
OTHER_UNITS + AREA_UNITS + DISTANCE_UNITS + VOLUME_UNITS + WEIGHT_UNITS
-
end
-
-
1
def self.si_units
-
OTHER_UNITS + SI_UNITS
-
end
-
-
1
def self.usc_units
-
OTHER_UNITS + USC_UNITS
-
end
-
-
# Check to see if a measurement unit is valid
-
1
def self.valid? uom
-
5
Unitwise.valid? uom
-
end
-
-
# Convert a quantity from one unit to another
-
5
def self.convert(quantity, from_uom, to_uom)
-
begin
-
4
Unitwise(quantity, from_uom).convert_to(to_uom).to_f
-
1
rescue Exception
-
1
raise ArgumentError.new('invalid argument')
-
end
-
end
-
-
end
-
# Include the FileSizevalidator mixin
-
1
require 'file_size_validator'
-
-
1
class Upload < ActiveRecord::Base
-
-
# From system config
-
1
MAX_UPLOAD_FILE_SIZE = Rails.application.config.max_upload_file_size
-
-
# Include the object key mixin
-
1
include TransamObjectKey
-
-
#-----------------------------------------------------------------------------
-
# Callbacks
-
#-----------------------------------------------------------------------------
-
1
after_initialize :set_defaults
-
1
before_destroy :unassociate_assets_and_events
-
-
# Associations
-
1
belongs_to :user, -> { unscope(where: :active) }
-
1
belongs_to :organization
-
1
belongs_to :file_status_type
-
1
belongs_to :file_content_type
-
# Asset events and assets can be created by bulk update
-
1
has_many :asset_events
-
1
has_many :assets, class_name: Rails.application.config.asset_base_class_name
-
-
# uploader
-
1
mount_uploader :file, ExcelUploader
-
-
43
validates :organization_id, :presence => true, unless: Proc.new { |u| u.file_content_type_id == FileContentType.find_by(name: 'New Inventory').id }
-
1
validates :user_id, :presence => true
-
1
validates :file_status_type_id, :presence => true
-
1
validates :file_content_type_id, :presence => true
-
1
validates :file, :presence => true, :file_size => { :maximum => MAX_UPLOAD_FILE_SIZE.megabytes.to_i }
-
1
validates :original_filename, :presence => true
-
-
# default scope
-
45
default_scope { order('created_at DESC') }
-
1
scope :new_files, -> { where('file_status_type_id = ?', FileStatusType.find_by_name('Unprocessed').id) }
-
-
# List of hash parameters allowed by the controller
-
1
FORM_PARAMS = [
-
:organization_id,
-
:user_id,
-
:file_status_type_id,
-
:file_content_type_id,
-
:file,
-
:original_filename,
-
:force_update,
-
:num_rows_processed,
-
:num_rows_added,
-
:num_rows_replaced,
-
:num_rows_failed,
-
:processing_log,
-
:processing_completed_at,
-
:processing_started_at
-
]
-
-
#-----------------------------------------------------------------------------
-
#
-
# Class Methods
-
#
-
#-----------------------------------------------------------------------------
-
-
1
def self.allowable_params
-
2
FORM_PARAMS
-
end
-
-
#-----------------------------------------------------------------------------
-
#
-
# Instance Methods
-
#
-
#-----------------------------------------------------------------------------
-
-
# Returns the number of seconds needed to process the file
-
1
def processing_time
-
4
if processing_completed_at.present? and processing_started_at.present?
-
1
processing_completed_at - processing_started_at
-
end
-
end
-
-
1
def organizations
-
Organization.where(id: assets.pluck(:organization_id))
-
end
-
-
1
def can_resubmit?
-
3
return false if new_record?
-
2
return false if file_status_type_id < 3
-
1
return true
-
end
-
1
def can_delete?
-
3
return false if new_record?
-
2
return false if file_status_type_id < 3
-
1
return true
-
end
-
-
# Resets the state of the upload and destroys dependent events
-
1
def reset
-
-
# you cannot undo changes if you forced them
-
2
unless force_update
-
2
self.file_status_type_id = FileStatusType.find_by_name('Unprocessed').id
-
2
self.num_rows_processed = nil
-
2
self.num_rows_added = nil
-
2
self.num_rows_replaced = nil
-
2
self.num_rows_failed = nil
-
2
self.num_rows_skipped = nil
-
2
self.processing_log = nil
-
2
self.processing_completed_at = nil
-
2
self.processing_started_at = nil
-
2
asset_events.destroy_all
-
2
assets.destroy_all
-
end
-
end
-
-
1
def updates
-
1
updates = []
-
-
1
if assets.empty?
-
1
asset_events.each do |evt|
-
1
updates << [evt.send(Rails.application.config.asset_base_class_name.underscore), evt]
-
end
-
else
-
assets.each do |a|
-
events = a.asset_events.where(upload_id: id)
-
if events.empty?
-
updates << [a, nil]
-
else
-
events.each do |evt|
-
updates << [a, evt]
-
end
-
end
-
end
-
end
-
-
1
updates
-
end
-
-
1
protected
-
-
# Set resonable defaults for a new vehicle
-
1
def set_defaults
-
44
self.force_update = self.force_update.nil? ? false : self.force_update
-
44
self.file_status_type_id ||= FileStatusType.find_by_name('Unprocessed').id
-
end
-
-
# Destroying an upload only removes the upload, events remain, but must be unassociated
-
1
def unassociate_assets_and_events
-
1
assets.update_all(upload_id: nil)
-
1
asset_events.update_all(upload_id: nil)
-
end
-
-
end
-
#-------------------------------------------------------------------------------
-
# User
-
#
-
# Base class for all users. This class represents a generic user.
-
#-------------------------------------------------------------------------------
-
1
class User < ActiveRecord::Base
-
1
acts_as_token_authenticatable
-
-
# Enable user roles for this use
-
1
rolify
-
-
# Include default devise modules. Others available are:
-
# :confirmable, :lockable, :timeoutable and :omniauthable
-
1
devise :database_authenticatable, :lockable, :recoverable, :rememberable, :trackable, :validatable, :timeoutable
-
-
# Include the object key mixin
-
1
include TransamObjectKey
-
-
1
include TransamTokenAuthentication
-
-
#-----------------------------------------------------------------------------
-
# Callbacks
-
#-----------------------------------------------------------------------------
-
1
after_initialize :set_defaults
-
-
# Clean up any HABTM associations before the user is destroyed
-
209
before_destroy { :clean_habtm_relationships }
-
-
#-----------------------------------------------------------------------------
-
# Associations
-
#-----------------------------------------------------------------------------
-
-
1336
has_many :users_roles, -> { active }, :inverse_of => :user
-
1
has_many :roles, :through => :users_roles
-
-
# every user belongs to a single organizations
-
1
belongs_to :organization
-
-
# Every user can have a weather code associated with their city. This is used
-
# to display local weather on the dashboard
-
1
belongs_to :weather_code
-
-
# Every user has 0 or 1 user organization filter that they are using and a list that they own
-
1
belongs_to :user_organization_filter
-
1
has_and_belongs_to_many :user_organization_filters, :join_table => 'users_user_organization_filters'
-
-
# every user has access to 0 or more organizations for reporting
-
1
has_and_belongs_to_many :organizations, :join_table => 'users_organizations'
-
1
has_and_belongs_to_many :viewable_organizations, :join_table => 'users_viewable_organizations', :class_name => 'Organization'
-
-
1
has_many :organization_users, -> {distinct}, through: :organizations, :source => 'users'
-
-
# Every user can have 0 or more messages
-
1
has_many :messages
-
-
# Every user can have 0 or tasks assigned to them
-
1
has_many :tasks, :foreign_key => :assigned_to_user_id
-
-
# Every user can have a profile picture
-
1
has_many :images, :as => :imagable, :dependent => :destroy
-
-
-
# Messages that have been tagged by the user
-
1
has_many :message_tags
-
1
has_many :messages, :through => :message_tags
-
-
# Notifications
-
1
has_many :user_notifications
-
-
# Assets that have been tagged by the user
-
1
has_many :asset_tags
-
1
has_many :assets, :through => :asset_tags
-
-
# AssetEvents that have been tagged by the user
-
1
has_many :asset_events, :foreign_key => :created_by_id
-
-
# Deprecated saved search
-
1
has_many :saved_searches
-
-
# New saved query
-
1
has_many :saved_queries, :foreign_key => :created_by_user_id
-
-
#-----------------------------------------------------------------------------
-
# Transients
-
#-----------------------------------------------------------------------------
-
-
#-----------------------------------------------------------------------------
-
# Validations
-
#-----------------------------------------------------------------------------
-
1
validates :first_name, :presence => true, :length => { maximum: 64 }
-
1
validates :last_name, :presence => true, :length => { maximum: 64 }
-
1
validates :external_id, :allow_nil => true, :length => { maximum: 32 }
-
1
validates :title, :allow_nil => true, :length => { maximum: 64 }
-
-
#validates :email, :presence => true, :length => { maximum: 128 }, :uniqueness => true, :format => { :with => /\A[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]+\z/, :message => "email address is not valid" }
-
1
validates :phone, :presence => true, :length => { maximum: 12 }
-
1
validates :phone_ext, :allow_nil => true, :length => { maximum: 6 }
-
1
validates :timezone, :presence => true, :length => { maximum: 32 }
-
-
1
validates :address1, :allow_nil => true, :length => { maximum: 32 }
-
1
validates :address2, :allow_nil => true, :length => { maximum: 32 }
-
1
validates :city, :allow_nil => true, :length => { maximum: 32 }
-
1
validates :state, :allow_nil => true, :length => { maximum: 2 }
-
1
validates :zip, :allow_nil => true, :length => { maximum: 12 }
-
-
1
validates :num_table_rows,:presence => true, :numericality => {:only_integer => true, :greater_than_or_equal_to => 5}
-
1
validates :organization, :presence => true
-
-
#-----------------------------------------------------------------------------
-
# Scopes
-
#-----------------------------------------------------------------------------
-
# Scope only active users
-
2002
scope :active, -> { where(active: true) }
-
-
# default scope
-
2000
default_scope { active.order(:last_name) }
-
-
-
#-----------------------------------------------------------------------------
-
# Lists
-
#-----------------------------------------------------------------------------
-
1
SEARCHABLE_FIELDS = [
-
:first_name,
-
:last_name,
-
:email,
-
:phone,
-
:title,
-
]
-
-
# List of allowable form param hash keys
-
1
FORM_PARAMS = [
-
:organization_id,
-
:first_name,
-
:last_name,
-
:phone,
-
:phone_ext,
-
:timezone,
-
:email,
-
:title,
-
:notify_via_email,
-
:password,
-
:password_confirmation,
-
:current_password,
-
:remember_me,
-
:external_id,
-
:num_table_rows,
-
:weather_code_id,
-
:active,
-
:address1,
-
:address2,
-
:city,
-
:state,
-
:zip,
-
:role_ids,
-
:privilege_ids,
-
:user_organization_filter_id,
-
:organization_ids
-
]
-
-
#-----------------------------------------------------------------------------
-
# Class Methods
-
#-----------------------------------------------------------------------------
-
-
1
def self.allowable_params
-
7
FORM_PARAMS
-
end
-
-
#-----------------------------------------------------------------------------
-
# Instance Methods
-
#-----------------------------------------------------------------------------
-
-
# Returns the default weather code for the users dashboard
-
1
def default_weather_code
-
if weather_code
-
weather_code.code
-
else
-
SystemConfig.instance.default_weather_code
-
end
-
end
-
-
# Get the new messages for the current user
-
1
def new_messages
-
1
Message.where('to_user_id = ? AND opened_at IS NULL', id).order("created_at DESC")
-
end
-
-
# Gets the tasks for the current user that are still open
-
1
def assigned_tasks
-
1
Task.where("assigned_to_user_id = ? AND state IN (?)", id, ["new", "started", "halted"]).order(:complete_by)
-
end
-
1
def due_tasks
-
today = Date.today
-
assigned_tasks.where("complete_by < ?", Date.today.end_of_day)
-
end
-
1
def new_tasks
-
assigned_tasks.where(:state => "new")
-
end
-
1
def started_tasks
-
assigned_tasks.where(:state => "started")
-
end
-
1
def on_hold_tasks
-
assigned_tasks.where(:state => "halted")
-
end
-
-
1
def all_searches(search_type_id=nil)
-
if search_type_id
-
saved_searches.where(search_type_id: search_type_id) + searches_shared_with_me.where(search_type_id: search_type_id)
-
else
-
saved_searches + searches_shared_with_me
-
end
-
end
-
-
1
def shared_searches
-
saved_searches.joins(:organizations)
-
end
-
-
1
def searches_shared_with_me
-
SavedSearch.joins(:organizations).where(organizations: {id: organizations.ids}).where.not(saved_searches: {user_id: self.id})
-
end
-
-
# Returns the initials for this user
-
1
def get_initials
-
3
"#{first_name[0]}#{last_name[0]}".upcase
-
end
-
-
# Getter for privileges
-
1
def privileges
-
5
roles.privileges
-
end
-
# Getter for privileges
-
1
def privilege_ids
-
5
a = []
-
5
privileges.each{|x| a << x.id}
-
5
a
-
end
-
-
# Returns the user's primary role. This is the role which has the highest
-
# weight. User < Manager < Regional Manager < CMO < CEO where CEO has the
-
# highest weight
-
1
def primary_role
-
3
roles.roles.order(:weight).last
-
end
-
-
# Returns true if the user is in a specified role, false otherwise
-
1
def is_in_role(role_id)
-
4
! roles.find(role_id).nil?
-
end
-
-
# Returns true if the user has at least one of the roles. Roles can be strings
-
# or symbols
-
1
def is_in_roles?(roles_to_test)
-
7
roles_to_test.each do |name|
-
7
if roles_name.include? name.to_s
-
4
return true
-
end
-
end
-
3
false
-
end
-
-
1
def to_s
-
67
name
-
end
-
-
1
def name
-
1149
"#{first_name} #{last_name}"
-
end
-
-
# This method defines required fields for any model which is geocodable
-
1
def full_address
-
8
elems = []
-
8
elems << address1 unless address1.blank?
-
8
elems << address2 unless address2.blank?
-
8
elems << city unless city.blank?
-
8
elems << state unless state.blank?
-
8
elems << zip unless zip.blank?
-
8
elems.compact.join(', ')
-
end
-
-
1
def searchable_fields
-
1711
SEARCHABLE_FIELDS
-
end
-
-
1
def update_user_organization_filters
-
3
self.user_organization_filters = UserOrganizationFilter.joins(:users).where(created_by_user_id: self.id).sorted.group('user_organization_filters.id').having( 'count( user_id ) = 1' )
-
-
3
UserOrganizationFilter.where('resource_type IS NOT NULL').each do |filter|
-
puts self.try(filter.resource_type.downcase.pluralize).include? filter.resource
-
puts self.organizations.inspect
-
if self.respond_to? filter.resource_type.downcase.pluralize #check has many associations
-
if self.try(filter.resource_type.downcase.pluralize).include? filter.resource
-
self.user_organization_filters << filter
-
end
-
elsif self.respond_to? filter.resource_type.downcase # check single association
-
if self.try(filter.resource_type.downcase) == filter.resource
-
self.user_organization_filters << filter
-
end
-
end
-
end
-
-
3
if self.user_organization_filters.system_filters.count == 0
-
3
f = UserOrganizationFilter.new({:active => 1, :name => "#{self.name}'s organizations", :description => "#{self.name}'s organizations", :sort_order => 1})
-
3
f.users = [self]
-
3
f.creator = User.find_by(first_name: 'system')
-
3
f.query_string = Organization.active.joins(:users).where('users_organizations.user_id = ?', self.id).to_sql
-
3
f.save!
-
end
-
-
3
if self.user_organization_filter.nil? || !(self.user_organization_filters.include? self.user_organization_filter)
-
3
self.user_organization_filter = self.user_organization_filters.system_filters.first
-
end
-
-
3
self.save!
-
end
-
-
#-----------------------------------------------------------------------------
-
# Devise hooks
-
#-----------------------------------------------------------------------------
-
-
# check if the user is active and can log in
-
1
def active_for_authentication?
-
# Log it
-
154
unless self.active
-
Rails.logger.info "Attempted access to in-active account for user with email #{email} at #{Time.now}"
-
end
-
-
154
super && self.active
-
end
-
-
1
def inactive_message
-
"Sorry, this account has been deactivated."
-
end
-
-
# Overrides for logging account locks/unlocks
-
1
def lock_access!
-
1
super
-
# Send a message to the admins that the account has been locked
-
1
Delayed::Job.enqueue LockedAccountInformerJob.new(object_key) unless new_record?
-
# Log it
-
1
Rails.logger.info "Locking account for user with email #{email} at #{Time.now}"
-
end
-
1
def unlock_access!
-
1
super
-
1
Rails.logger.info "Unlocking account for user with email #{email} at #{Time.now}"
-
end
-
#-----------------------------------------------------------------------------
-
-
#-----------------------------------------------------------------------------
-
# Protected Methods
-
#-----------------------------------------------------------------------------
-
1
protected
-
-
# Set resonable defaults for a new user
-
1
def set_defaults
-
1632
self.timezone ||= Rails.application.config.time_zone
-
1632
self.state ||= SystemConfig.instance.default_state_code
-
1632
self.num_table_rows ||= 10
-
1632
self.notify_via_email ||= false
-
1632
self.failed_attempts ||= 0
-
1632
self.active ||= false
-
end
-
-
1
def clean_habtm_relationships
-
organizations.clear
-
viewable_organizations.clear
-
end
-
-
#-----------------------------------------------------------------------------
-
# Private Methods
-
#-----------------------------------------------------------------------------
-
1
private
-
-
end
-
1
class UserNotification < ActiveRecord::Base
-
-
# Associations
-
1
belongs_to :user
-
1
belongs_to :notification
-
-
1
scope :unopened, -> { joins('INNER JOIN notifications ON notification_id = notifications.id').where('opened_at IS NULL AND notifications.active = true') }
-
1
scope :opened, -> { where('opened_at IS NOT NULL') }
-
-
end
-
1
class UserOrganizationFilter < ActiveRecord::Base
-
-
# Include the unique key mixin
-
1
include TransamObjectKey
-
-
# Callbacks
-
1
after_initialize :set_defaults
-
1
before_destroy :reset_users_using_filter
-
-
# Clean up any HABTM associations before the asset is destroyed
-
#before_destroy { :clean_habtm_relationships }
-
-
1
belongs_to :resource, :polymorphic => true
-
-
# Each filter is created by someone usually the owner but sometimes the system user (could be extended to sharing filters)
-
4
belongs_to :creator, -> { unscope(where: :active) }, :class_name => "User", :foreign_key => :created_by_user_id
-
-
# Each filter can have a list of organizations that are included
-
1
has_and_belongs_to_many :organizations, :join_table => 'user_organization_filters_organizations'
-
-
1
has_and_belongs_to_many :users, :join_table => 'users_user_organization_filters'
-
-
1
validates :name, :presence => true
-
1
validates :description, :presence => true
-
#validate :require_at_least_one_organization
-
-
# Allow selection of active instances
-
1
scope :active, -> { where(:active => true) }
-
# sorting rule: 1. first sort ASC based on sort_order; 2. for those without sort_order, sort by name ASC
-
148
scope :sorted, -> { order(Arel.sql('sort_order IS NULL, sort_order ASC'), :name) }
-
-
# Named Scopes
-
153
scope :system_filters, -> { where(created_by_user_id: 1, active: 1) }
-
3
scope :other_filters, -> { where.not(created_by_user_id: 1).where(active: 1) }
-
-
# List of allowable form param hash keys
-
1
FORM_PARAMS = [
-
:name,
-
:description,
-
:organization_ids
-
]
-
-
#------------------------------------------------------------------------------
-
#
-
# Class Methods
-
#
-
#------------------------------------------------------------------------------
-
-
1
def self.allowable_params
-
7
FORM_PARAMS
-
end
-
-
#------------------------------------------------------------------------------
-
#
-
# Instance Methods
-
#
-
#------------------------------------------------------------------------------
-
-
# Returns true if this is a system filter
-
1
def system_filter?
-
7
UserOrganizationFilter.system_filters.include? self
-
end
-
-
1
def shared?
-
1
self.users.count > 1
-
end
-
-
1
def get_organizations
-
5
self.query_string.present? ? Organization.find_by_sql(self.query_string) : self.organizations
-
end
-
-
1
def can_update? user
-
3
!self.system_filter? && (self.users.include? user)
-
end
-
-
1
def can_destroy? user
-
1
!self.system_filter? && (self.users.include? user) && self != user.user_organization_filter
-
end
-
-
#------------------------------------------------------------------------------
-
#
-
# Protected Methods
-
#
-
#------------------------------------------------------------------------------
-
-
1
protected
-
-
1
def require_at_least_one_organization
-
if organizations.count == 0
-
errors.add(:organizations, "must be selected.")
-
return false
-
end
-
end
-
-
1
def clean_habtm_relationships
-
organizations.clear
-
end
-
-
1
private
-
# Set resonable defaults for a new filter
-
1
def set_defaults
-
2840
self.active = self.active.nil? ? true : self.active
-
end
-
-
1
def reset_users_using_filter
-
142
User.where(user_organization_filter_id: self.id).each do |user|
-
137
user.update(user_organization_filter_id: user.user_organization_filters.system_filters.sorted.first.try(:id))
-
end
-
-
end
-
-
-
end
-
#-------------------------------------------------------------------------------
-
# UserRole
-
#
-
# User Role join table -- maps users to roles and vis versa. Provides additional
-
# ability to track when a role was added/updated, acrive roles, and who added
-
# or activated the role
-
#-------------------------------------------------------------------------------
-
1
class UsersRole < ActiveRecord::Base
-
-
#-----------------------------------------------------------------------------
-
# Callbacks
-
#-----------------------------------------------------------------------------
-
1
after_initialize :set_defaults
-
-
#-----------------------------------------------------------------------------
-
# Associations
-
#-----------------------------------------------------------------------------
-
-
# Every user role belongs to exactly one user
-
1
belongs_to :user
-
-
# Every user role belongs to exactly one role
-
1
belongs_to :role
-
-
# Every user role can have a granted by user
-
1
belongs_to :granted_by_user, :class_name => "User", :foreign_key => :granted_by_user_id
-
-
# Every user role can have a revoked by user
-
1
belongs_to :revoked_by_user, :class_name => 'User', :foreign_key => :revoked_by_user_id
-
-
#-----------------------------------------------------------------------------
-
# Validations
-
#-----------------------------------------------------------------------------
-
1
validates :user, :presence => true
-
1
validates :role, :presence => true
-
-
#-----------------------------------------------------------------------------
-
# Scopes
-
#-----------------------------------------------------------------------------
-
3514
default_scope { order(:created_at) }
-
-
# tasks which are currently active based on the workflow
-
1336
scope :active, -> { where(:active => true) }
-
-
# List of allowable form param hash keys
-
1
FORM_PARAMS = [
-
:id,
-
:user_id,
-
:role_id,
-
:granted_by_user_id,
-
:revoked_by_user_id,
-
]
-
-
#-----------------------------------------------------------------------------
-
# Class Methods
-
#-----------------------------------------------------------------------------
-
-
1
def self.allowable_params
-
1
FORM_PARAMS
-
end
-
-
#-----------------------------------------------------------------------------
-
# Instance Methods
-
#-----------------------------------------------------------------------------
-
-
#-----------------------------------------------------------------------------
-
# Protected Methods
-
#-----------------------------------------------------------------------------
-
1
protected
-
-
# Set resonable defaults for a new user role
-
1
def set_defaults
-
1113
self.active ||= false
-
end
-
-
#-----------------------------------------------------------------------------
-
# Private Methods
-
#-----------------------------------------------------------------------------
-
-
-
end
-
#------------------------------------------------------------------------------
-
#
-
# Vendor
-
#
-
# Represents a vendor that assets or services have been purchased from
-
#
-
#------------------------------------------------------------------------------
-
1
class Vendor < ActiveRecord::Base
-
-
# Include the object key mixin
-
1
include TransamObjectKey
-
-
#------------------------------------------------------------------------------
-
# Callbacks
-
#------------------------------------------------------------------------------
-
1
after_initialize :set_defaults
-
-
#------------------------------------------------------------------------------
-
# Associations common to all vendors
-
#------------------------------------------------------------------------------
-
-
# Every vendor is owned by an organization
-
1
belongs_to :organization
-
-
# Every vendor has 0 or more assets
-
1
has_many :assets
-
-
#------------------------------------------------------------------------------
-
# Validations common to all vendors
-
#------------------------------------------------------------------------------
-
1
validates :name, :presence => true
-
1
validates :organization, :presence => true
-
-
# List of allowable form param hash keys
-
1
FORM_PARAMS = [
-
:name,
-
:organization_id,
-
:address1,
-
:address2,
-
:city,
-
:state,
-
:zip,
-
:phone,
-
:fax,
-
:url,
-
:active,
-
:latitude,
-
:longitude
-
]
-
-
1
SEARCHABLE_FIELDS = [
-
:object_key,
-
:name,
-
:address1,
-
:address2,
-
:city,
-
:state,
-
:zip,
-
:phone,
-
:fax,
-
:url
-
]
-
-
49
default_scope { order("name") }
-
-
2
scope :active, -> { where(:active => true) }
-
-
#------------------------------------------------------------------------------
-
#
-
# Class Methods
-
#
-
#------------------------------------------------------------------------------
-
-
1
def self.allowable_params
-
3
FORM_PARAMS
-
end
-
-
#------------------------------------------------------------------------------
-
#
-
# Instance Methods
-
#
-
#------------------------------------------------------------------------------
-
-
1
def to_s
-
3
name
-
end
-
-
1
def searchable_fields
-
37
SEARCHABLE_FIELDS
-
end
-
-
#------------------------------------------------------------------------------
-
#
-
# Protected Methods
-
#
-
#------------------------------------------------------------------------------
-
1
protected
-
-
# Set resonable defaults for a new organization
-
1
def set_defaults
-
47
self.active = self.active.nil? ? true : self.active
-
47
self.state ||= SystemConfig.instance.default_state_code
-
end
-
-
end
-
1
class WeatherCode < ActiveRecord::Base
-
-
# All codes that are available
-
3
scope :active, -> { where(:active => true) }
-
-
19
default_scope { order(:city) }
-
-
1
def self.search(state, city)
-
1
where('state = ? AND city = ?', state, city).first
-
end
-
-
1
def to_s
-
4
city
-
end
-
-
end
-
1
class WebBrowserType < ActiveRecord::Base
-
-
# All types that are available
-
1
scope :active, -> { where(:active => true) }
-
-
1
def to_s
-
1
name
-
end
-
-
end
-
#------------------------------------------------------------------------------
-
#
-
# WorkflowEvent
-
#
-
# A WorkflowEvent that has been associated with another class such as a Workorder, Asset etc. This is a
-
# polymorphic class that can store workflow events against any class that includes an
-
# accountable association
-
#
-
# To use this class as an association with another class include the following line into
-
# the model
-
#
-
# has_many :workflow_events, :as => :accountable, :dependent => :destroy
-
#
-
#------------------------------------------------------------------------------
-
1
class WorkflowEvent < ActiveRecord::Base
-
-
# Include the object key mixin
-
1
include TransamObjectKey
-
-
# Callbacks
-
1
after_initialize :set_defaults
-
-
# Associations
-
1
belongs_to :accountable, :polymorphic => true
-
-
2
belongs_to :creator, -> { unscope(where: :active) }, :class_name => 'User', :foreign_key => :created_by_id
-
-
# default scope
-
50
default_scope { order('created_at DESC') }
-
-
1
validates :event_type, :presence => true
-
1
validates :creator, :presence => true
-
-
# List of hash parameters allowed by the controller
-
1
FORM_PARAMS = [
-
:event_type,
-
:accountable_id,
-
:accountable_type,
-
:created_by_id
-
]
-
-
#------------------------------------------------------------------------------
-
#
-
# Class Methods
-
#
-
#------------------------------------------------------------------------------
-
-
1
def self.allowable_params
-
1
FORM_PARAMS
-
end
-
-
#------------------------------------------------------------------------------
-
#
-
# Protected Methods
-
#
-
#------------------------------------------------------------------------------
-
1
protected
-
-
# Set resonable defaults for a new asset event
-
1
def set_defaults
-
-
end
-
-
#------------------------------------------------------------------------------
-
#
-
# Private Methods
-
#
-
#------------------------------------------------------------------------------
-
1
private
-
-
end
-
1
class AbstractReport
-
-
# From the application config
-
1
MAX_FORECASTING_YEARS = SystemConfig.instance.num_forecasting_years
-
1
MAX_REPORTING_YEARS = SystemConfig.instance.num_reporting_years
-
-
1
def initialize(attr = {})
-
4
attr.each do |k,v|
-
self.send "#{k}=", v if respond_to? k
-
end
-
end
-
-
end
-
class AssetAgeReport < AbstractReport
-
-
def initialize(attributes = {})
-
super(attributes)
-
end
-
-
def get_data(organization_id_list, params)
-
-
# Check to see if we got an asset type to sub select on
-
if params[:report_filter_type]
-
report_filter_type = params[:report_filter_type].to_i
-
else
-
report_filter_type = 0
-
end
-
-
a = []
-
asset_counts = []
-
table_labels = ['Years']
-
-
if report_filter_type > 0
-
asset_type = AssetType.find_by_id(report_filter_type)
-
table_labels << asset_type.name
-
else
-
AssetType.all.each do |at|
-
count = Rails.application.config.asset_base_class_name.constantize.where(organization_id: organization_id_list, asset_subtype_id: at.id).count
-
asset_counts << count
-
table_labels << at.name unless count == 0
-
end
-
end
-
-
(0..MAX_REPORTING_YEARS).each do |year|
-
counts = []
-
counts << "#{year}"
-
manufacture_year = year.year.ago.year
-
if report_filter_type > 0
-
counts << Rails.application.config.asset_base_class_name.constantize.where(organization_id: organization_id_list, asset_subtype_id: report_filter_type, manufacture_year: manufacture_year).count
-
else
-
AssetType.all.each_with_index do |type, idx|
-
counts << Rails.application.config.asset_base_class_name.constantize.where(organization_id: organization_id_list, asset_subtype_id: type.id, manufacture_year: manufacture_year).count unless asset_counts[idx] == 0
-
end
-
end
-
a << counts
-
end
-
-
# get the bucket for MAX_YEARS+ years old
-
year = MAX_REPORTING_YEARS
-
counts = []
-
counts << "> #{year}"
-
manufacture_year = MAX_REPORTING_YEARS.year.ago.year
-
if report_filter_type > 0
-
counts << Rails.application.config.asset_base_class_name.constantize.where(organization_id: organization_id_list, asset_subtype_id: report_filter_type, manufacture_year: manufacture_year).count
-
else
-
AssetType.all.each_with_index do |type, idx|
-
counts << Rails.application.config.asset_base_class_name.constantize.where(organization_id: organization_id_list, asset_subtype_id: type.id, manufacture_year: manufacture_year).count unless asset_counts[idx] == 0
-
end
-
end
-
a << counts
-
-
subheader = 'Age Count <a class="transam-popover" data-container="body" data-content="<p>Based on <i>In Service Date</i> of asset + 365 days.</p><p>e.g. <i>In Service Date</i> + 364 days = <1 year; <i>In Service Date</i> + 365 days = 1 year</p>" data-html="true" data-placement="bottom" data-title="Age" data-toggle="popover" tabindex="0" data-original-title="" title=""><i class="fa fa-info-circle text-info"></i></a>'
-
-
return {data: a, labels: table_labels, table_subheader: subheader, table_labels: table_labels, table_data: a, chart_labels: table_labels, chart_data: a, formats: [:integer, :integer, :integer, :integer, :integer, :integer, :integer, :integer, :integer, :integer, :integer, :integer]}
-
-
end
-
-
end
-
class AssetConditionReport < AbstractReport
-
-
def initialize(attributes = {})
-
super(attributes)
-
end
-
-
def get_data(organization_id_list, params)
-
-
# Check to see if we got an asset type to sub select on
-
if params[:report_filter_type]
-
report_filter_type = params[:report_filter_type].to_i
-
else
-
report_filter_type = 0
-
end
-
-
a = []
-
labels = ['Condition', 'Count']
-
-
ConditionType.all.each do |x|
-
if report_filter_type > 0
-
count = Asset.where("assets.organization_id IN (?) AND assets.asset_type_id = ? AND assets.reported_condition_type_id = ?", organization_id_list, report_filter_type, x.id).count
-
else
-
count = Asset.where("assets.organization_id IN (?) AND assets.reported_condition_type_id = ?", organization_id_list, x.id).count
-
end
-
a << [x.name, count]
-
end
-
-
return {:labels => labels, :data => a}
-
-
end
-
-
end
-
1
class AssetSubtypeReport < AbstractReport
-
-
1
def initialize(attributes = {})
-
4
super(attributes)
-
end
-
-
1
def summarize(assets, subtypes)
-
-
4
a = []
-
4
labels = ['Asset Subtype', 'Count']
-
-
4
subtypes.each do |x|
-
494
count = assets.where(asset_subtype_id: x.id).count
-
494
a << [x.name, count] unless count == 0
-
end
-
-
4
return {:labels => labels, :data => a}
-
end
-
-
1
def get_data_from_collection(assets)
-
4
summarize(assets, AssetSubtype.all)
-
end
-
-
1
def get_data(organization_id_list, params)
-
-
# Check to see if we got an asset type to sub select on
-
if params[:report_filter_type]
-
report_filter_type = params[:report_filter_type].to_i
-
else
-
report_filter_type = 0
-
end
-
-
if report_filter_type == 0
-
subtypes = AssetSubtype.all
-
else
-
subtypes = AssetSubtype.where(asset_type_id: report_filter_type)
-
end
-
-
assets = Rails.application.config.asset_base_class_name.constantize.where(organization_id: organization_id_list)
-
-
return summarize(assets, subtypes)
-
-
end
-
-
end
-
class AssetSubtypeReportRow < BasicReportRow
-
-
attr_accessor :asset_subtype
-
-
def initialize(asset_subtype)
-
super(asset_subtype)
-
self.asset_subtype = asset_subtype
-
end
-
-
end
-
class BacklogReport < AbstractReport
-
-
def initialize(attributes = {})
-
super(attributes)
-
end
-
-
def get_data(organization_id_list, params)
-
-
# Check to see if we got an asset type to sub select on
-
if params[:report_filter_type]
-
report_filter_type = params[:report_filter_type].to_i
-
else
-
report_filter_type = 0
-
end
-
-
# get the list of assets for this agency
-
if report_filter_type > 0
-
assets = Rails.application.config.asset_base_class_name.constantize.joins(asset_subtype: :asset_type).where(organization_id: organization_id_list, asset_types: {id: report_filter_type}, in_backlog: true).order('asset_types.id, asset_subtype_id')
-
else
-
assets = Rails.application.config.asset_base_class_name.constantize.where(organization_id: organization_id_list, in_backlog: true).order('asset_types.id, asset_subtype_id')
-
end
-
-
a = {}
-
assets.find_each do |asset|
-
# see if this asset sub type has been seen yet
-
if a.has_key?(asset.asset_subtype)
-
report_row = a[asset.asset_subtype]
-
else
-
report_row = AssetSubtypeReportRow.new(asset.asset_subtype)
-
a[asset.asset_subtype] = report_row
-
end
-
# get the replacement cost for this item based on the current policy
-
report_row.add(asset)
-
end
-
return a
-
end
-
-
end
-
class BasicReportRow
-
-
attr_accessor :key, :count, :replacement_cost, :id_list, :cost_recovery
-
-
def initialize(key)
-
self.key = key
-
self.count = 0
-
self.replacement_cost = 0
-
self.cost_recovery = 0
-
self.id_list = []
-
end
-
-
def add(asset)
-
self.count += 1
-
self.replacement_cost += asset.scheduled_replacement_cost unless asset.scheduled_replacement_cost.nil?
-
self.id_list << asset.object_key
-
end
-
-
end
-
class CapitalNeedsReport < NeedsReport
-
-
BACKLOG_KEY = 'Backlog'
-
-
def initialize(attributes = {})
-
super(attributes)
-
end
-
-
def get_data(organization_id_list, params)
-
-
# Check to see if we got an asset type to sub select on
-
if params[:report_filter_type]
-
report_filter_type = params[:report_filter_type].to_i
-
else
-
report_filter_type = 0
-
end
-
-
# Asset needs by year
-
analysis_year = current_fiscal_year_year
-
last_year = last_fiscal_year_year
-
a = {}
-
report_row = BasicReportRow.new(BACKLOG_KEY)
-
a[BACKLOG_KEY] = report_row
-
# get the backlog assets
-
assets = get_backlog_assets(organization_id_list, report_filter_type)
-
assets.find_each do |asset|
-
report_row.add(asset)
-
end
-
-
(analysis_year..last_year).each do |year|
-
report_row = BasicReportRow.new(year)
-
a[year] = report_row
-
# get the assets for this analysis year
-
assets = get_assets(organization_id_list, year, report_filter_type)
-
assets.find_each do |asset|
-
report_row.add(asset)
-
end
-
end
-
-
return a
-
end
-
-
end
-
class CurrentNeedsReport < NeedsReport
-
-
def initialize(attributes = {})
-
super(attributes)
-
end
-
-
def get_data(organization_id_list, params)
-
-
# Check to see if we got an asset type to sub select on
-
if params[:report_filter_type]
-
report_filter_type = params[:report_filter_type].to_i
-
else
-
report_filter_type = 0
-
end
-
-
analysis_year = Date.today.year
-
-
assets = get_assets(organization_id_list, analysis_year, report_filter_type)
-
-
return calc_need(assets)
-
-
end
-
-
end
-
class CustomSqlReport < AbstractReport
-
-
def initialize(attributes = {})
-
super(attributes)
-
end
-
-
def get_data(organization_id_list, params)
-
-
# Check to see if we got an asset type to sub select on
-
if params[:report_filter_type]
-
report_filter_type = params[:report_filter_type].to_i
-
else
-
report_filter_type = 0
-
end
-
-
if report_filter_type == 0
-
subtypes = AssetSubtype.all
-
else
-
subtypes = AssetSubtype.where('asset_type_id = ?', report_filter_type)
-
end
-
-
# Execute a SQL query against the database
-
results = ActiveRecord::Base.connection.exec_query(params[:sql])
-
-
a = []
-
labels = []
-
-
if results.empty?
-
a = []
-
labels = []
-
else
-
# Get the column names that were returned
-
labels = results.columns.dup
-
a = results.rows.dup
-
end
-
-
return {labels: labels, data: a, table_labels: labels, table_data: a, chart_labels: labels, chart_data: a}
-
-
end
-
-
end
-
class IssuesReport < AbstractReport
-
-
def initialize(attributes = {})
-
super(attributes)
-
end
-
-
def get_data(organization_id_list, params)
-
-
Issue.all.order(:created_at, :issue_type_id)
-
-
end
-
-
end
-
class NeedsReport < AbstractReport
-
-
# Include the fiscal year mixin
-
include FiscalYear
-
-
def initialize(attributes = {})
-
super(attributes)
-
end
-
-
protected
-
-
def get_assets(organization_id_list, analysis_year, report_filter_type)
-
-
# get the list of assets for this organization
-
if report_filter_type > 0
-
assets = Asset.where('assets.organization_id IN (?) AND assets.asset_type_id = ? AND assets.policy_replacement_year = ?', organization_id_list, report_filter_type, analysis_year)
-
else
-
assets = Asset.where('assets.organization_id IN (?) AND assets.policy_replacement_year = ?', organization_id_list, analysis_year)
-
end
-
return assets
-
-
end
-
-
def get_backlog_assets(organization_id_list, report_filter_type)
-
-
# get the list of assets for this agency
-
if report_filter_type > 0
-
assets = Asset.where('assets.organization_id IN (?) AND assets.asset_type_id = ? AND assets.in_backlog = ?', organization_id_list, report_filter_type, true)
-
else
-
assets = Asset.where("assets.organization_id IN (?) AND assets.in_backlog = ?", organization_id_list, true)
-
end
-
return assets
-
-
end
-
-
# Calculates need for this year based on a list of assets
-
def calc_need(assets)
-
-
a = {}
-
assets.find_each do |asset|
-
# see if this asset sub type has been seen yet
-
if a.has_key?(asset.asset_subtype)
-
report_row = a[asset.asset_subtype]
-
else
-
report_row = AssetSubtypeReportRow.new(asset.asset_subtype)
-
a[asset.asset_subtype] = report_row
-
end
-
# get the replacement cost for this item based on the current policy
-
report_row.add(asset)
-
end
-
-
return a
-
end
-
-
end
-
class NextFyNeedsReport < NeedsReport
-
-
def initialize(attributes = {})
-
super(attributes)
-
end
-
-
def get_data(organization_id_list, params)
-
-
# Check to see if we got an asset type to sub select on
-
if params[:report_filter_type]
-
report_filter_type = params[:report_filter_type].to_i
-
else
-
report_filter_type = 0
-
end
-
-
analysis_year = Date.today.year + 1
-
-
assets = get_assets(organization_id_list, analysis_year, report_filter_type)
-
-
return calc_need(assets)
-
-
end
-
-
end
-
class ServiceLifeConsumedReport < AbstractReport
-
-
BUCKET_SIZE = 10
-
MAX_PCNT = 200
-
-
def initialize(attributes = {})
-
super(attributes)
-
end
-
-
def get_data(organization_id_list, params)
-
-
# Check to see if we got an asset type to sub select on
-
if params[:report_filter_type]
-
report_filter_type = params[:report_filter_type].to_i
-
else
-
report_filter_type = 0
-
end
-
-
a = []
-
-
# Set up the labels one for each asset class
-
labels = ['% Service Life Used']
-
# asset_cols is the index into the column array a[][x] for each asset type
-
asset_cols = []
-
if report_filter_type > 0
-
asset_type = AssetType.find_by_id(report_filter_type)
-
labels << asset_type.name
-
else
-
AssetType.all.each do |type|
-
asset_count = Asset.where("assets.organization_id IN (?) AND assets.asset_type_id = ?", organization_id_list, type.id).count
-
if asset_count > 0
-
# push this asset type into the a matrix
-
labels << type.name
-
asset_cols << labels.size - 1 # account for the row label
-
else
-
asset_cols << -1
-
end
-
end
-
end
-
# this is the number of asset types with assets
-
num_asset_types = labels.size - 1
-
-
#puts asset_cols.inspect
-
#puts labels.inspect
-
-
# Set up the buckets using BUCKET_SIZE buckets.The array is now an array of length num_buckets where each element
-
# is an array of ints with value 0 and length num_asset_types
-
num_buckets = MAX_PCNT / BUCKET_SIZE
-
(0..num_buckets).each do |bucket|
-
counts = []
-
counts << "#{bucket * BUCKET_SIZE}%"
-
(1..num_asset_types).each do |x|
-
counts << 0
-
end
-
a << counts
-
end
-
-
puts "num buckets = #{num_buckets}"
-
puts "a num rows = #{a.size}, a num cols = #{a[0].size}"
-
-
# Process the assets and increament the bucket counters based on the %age useful life consumed for
-
# each asset
-
if report_filter_type > 0
-
assets = Asset.where("assets.organization_id IN (?) AND asset_type_id = ?", organization_id_list, report_filter_type)
-
else
-
assets = Asset.where("assets.organization_id IN (?)", organization_id_list)
-
end
-
# count the number of assets so we can normalize the report
-
num_assets = 0
-
assets.each do |asset|
-
# Only on age right now
-
pcnt_consumed = asset.expected_useful_life > 0 ? ((asset.age.to_f / asset.expected_useful_life * 12) * 100.0) : 0.0
-
# Get the column for this asset type, if we only have one it is the first column
-
col = report_filter_type > 0 ? 1 : asset_cols[asset.asset_type_id - 1]
-
row = [(pcnt_consumed / BUCKET_SIZE).to_i - 1, num_buckets].min
-
#puts "row = #{row}, col = #{col}"
-
a[row][col] += 1
-
num_assets += 1
-
end
-
-
# normalize the table
-
(0..num_buckets).each do |row|
-
(1..num_asset_types).each do |col|
-
a[row][col] = a[row][col] / num_assets.to_f * 100
-
end
-
end
-
-
return {:labels => labels, :data => a}
-
-
end
-
-
end
-
class UserLoginReport < AbstractReport
-
-
def initialize(attributes = {})
-
super(attributes)
-
end
-
-
def get_data(organization_id_list, params)
-
-
-
{data: User.joins(:organization).where("organization_id IN (?) AND last_sign_in_at IS NOT NULL", organization_id_list).order(:organization_id, :first_name, :last_name).pluck('organizations.short_name', :first_name, :last_name, :sign_in_count, :last_sign_in_at, :locked_at)}
-
-
end
-
-
end
-
#
-
# Abstract base searcher class for searches
-
#
-
class BaseSearcher
-
-
# Every search must have a user as the searcher
-
attr_accessor :user
-
-
#performs ActiveRecord-like mass assignment of attributes
-
def initialize(attributes = {})
-
mass_assign(attributes)
-
end
-
-
# Caches the rows
-
def data
-
@data ||= perform_query
-
end
-
-
# Override this to return the name of the form to display
-
def form_view
-
end
-
-
# Override this to return a session cache variable for storing the
-
# results set for paging through the result lists
-
def cache_variable_name
-
end
-
-
# Override this to return use a session cache list for saved results
-
def cached_data(list)
-
@klass.where(object_key: list) if @klass
-
end
-
-
def cache_params_variable_name
-
"query_search_params_var"
-
end
-
-
# Override this to return the name of the results table to display
-
def results_view
-
end
-
-
def to_s
-
queries.to_sql
-
end
-
-
protected
-
-
#############################################################################
-
# Creation methods
-
#############################################################################
-
-
# requires each attribute in the hash to be names the same as the class property
-
def mass_assign(attributes)
-
attributes.each do |attribute, value|
-
respond_to?(:"#{attribute}=") && send(:"#{attribute}=", value)
-
end
-
end
-
-
-
#############################################################################
-
# Querying methods
-
#############################################################################
-
-
# Log query and prepare it for front-end
-
def perform_query
-
Rails.logger.info queries.to_sql
-
queries
-
end
-
-
# Reduce as in map-reduce- merges down array of queries into a single one
-
def queries(with_orgs=true)
-
condition_parts(with_orgs).reduce(:merge)
-
end
-
-
# Finds each method named *_conditions and runs it in the concrete class
-
# Returns an array of ActiveRecord::Relation objects
-
def condition_parts(with_orgs=true)
-
### Subclass MUST respond with at least 1 non-nil AR::Relation object ###
-
-
if with_orgs
-
conditions = private_methods.grep(/_conditions$/)
-
else
-
conditions = (private_methods.grep(/_conditions$/) - [:organization_conditions])
-
end
-
conditions.map { |m| send(m) }.compact
-
end
-
-
def remove_blanks(input)
-
output = (input.is_a?(Array) ? input : [input])
-
output.select { |e| !e.blank? }
-
end
-
-
end
-
# Asset map searcher in core engine
-
#
-
module CoreAssetMapSearchable
-
-
extend ActiveSupport::Concern
-
-
included do
-
-
attr_accessor :organization_id, :asset_subtype_id, :asset_tag, :condition_rating_slider, :purchase_year_slider, :scheduled_replacement_year_slider
-
-
end
-
-
#------------------------------------------------------------------------------
-
#
-
# Class Methods
-
#
-
#------------------------------------------------------------------------------
-
module ClassMethods
-
-
end
-
#------------------------------------------------------------------------------
-
#
-
# Instance Methods
-
#
-
#------------------------------------------------------------------------------
-
-
private
-
-
def organization_id_conditions
-
# This method works with both individual inputs for organization_id as well
-
# as arrays containing several organization ids.
-
-
clean_organization_id = remove_blanks(organization_id)
-
@klass.where(organization_id: clean_organization_id)
-
end
-
-
def asset_subtype_id_conditions
-
clean_asset_subtype_id = remove_blanks(asset_subtype_id)
-
@klass.where(asset_subtype_id: clean_asset_subtype_id) unless clean_asset_subtype_id.empty?
-
end
-
-
def asset_tag_conditions
-
@klass.where("asset_tag LIKE ?", "%#{asset_tag}%") unless asset_tag.blank?
-
end
-
-
def condition_rating_conditions
-
unless condition_rating_slider.blank?
-
condition_ratings = condition_rating_slider.split(',')
-
@klass.joins('LEFT JOIN recent_asset_events_views AS recent_rating ON recent_rating.base_transam_asset_id = transam_assets.id AND recent_rating.asset_event_name = "Condition"').joins('LEFT JOIN asset_events AS rating_event ON rating_event.id = recent_rating.asset_event_id').where(rating_event: {assessed_rating: condition_ratings[0].to_i..condition_ratings[1].to_i}) unless condition_ratings.empty?
-
end
-
end
-
-
def purchase_year_conditions
-
unless purchase_year_slider.blank?
-
purchase_years = purchase_year_slider.split(',')
-
@klass.where(purchase_date: Date.new(purchase_years[0].to_i,1,1)..Date.new(purchase_years[1].to_i,12,31)) unless purchase_years.empty?
-
end
-
end
-
-
def scheduled_replacement_year_conditions
-
unless scheduled_replacement_year_slider.blank?
-
replacement_years = scheduled_replacement_year_slider.split(',')
-
@klass.where(scheduled_replacement_year: replacement_years[0].to_i..replacement_years[1].to_i) unless replacement_years.empty?
-
end
-
end
-
-
end
-
# Inventory searcher.
-
# Designed to be populated from a search form using a new/create controller model.
-
#
-
class OrganizationSearcher < BaseSearcher
-
-
# From the application config
-
ASSET_BASE_CLASS_NAME = Rails.application.config.asset_base_class_name
-
-
# add any search params to this list
-
attr_accessor :district_id,
-
:organization_type_id,
-
:keywords
-
-
-
# Return the name of the form to display
-
def form_view
-
'organization_search_form'
-
end
-
# Return the name of the results table to display
-
def results_view
-
'organization_search_results_table'
-
end
-
-
def cache_variable_name
-
OrganizationsController::INDEX_KEY_LIST_VAR
-
end
-
-
def initialize(attributes = {})
-
super(attributes)
-
end
-
-
private
-
-
def organization_type_conditions
-
unless organization_type_id.blank?
-
Organization.where(organization_type_id: organization_type_id)
-
else
-
Organization.where(id: user.user_organization_filter.get_organizations.map{|o| o.id})
-
end
-
end
-
-
def keyword_conditions
-
Organization.where("organizations.name LIKE ?", "%#{keywords}%") unless keywords.blank?
-
end
-
end
-
# Inventory searcher.
-
# Designed to be populated from a search form using a new/create controller model.
-
#
-
class UserSearcher < BaseSearcher # TODO Not Implemented. Just copied from other models
-
-
# add any search params to this list
-
attr_accessor :organization_id,
-
:keywords
-
-
# Return the name of the form to display
-
def form_view
-
'user_search_form'
-
end
-
# Return the name of the results table to display
-
def results_view
-
'user_search_results_table'
-
end
-
-
def cache_variable_name
-
UsersController::INDEX_KEY_LIST_VAR
-
end
-
-
def initialize(attributes = {})
-
super(attributes)
-
end
-
-
private
-
-
# Add any new conditions here. The property name must end with _conditions
-
def organization_conditions
-
if organization_id.blank?
-
["assets.organization_id in (?)", user.user_organization_filter.get_organizations.map{|o| o.id}]
-
else
-
["assets.organization_id = ?", organization_id]
-
end
-
end
-
#---------------------------------------------------
-
# FTA Reporting Characteristics
-
#---------------------------------------------------
-
def fta_funding_type_conditions
-
["assets.fta_funding_type_id = ?", fta_funding_type_id] unless fta_funding_type_id.blank?
-
end
-
def fta_ownership_type_conditions
-
["assets.fta_ownership_type_id = ?", fta_ownership_type_id] unless fta_ownership_type_id.blank?
-
end
-
def fta_vehicle_type_id_conditions
-
["assets.fta_vehicle_type_id = ?", fta_vehicle_type_id] unless fta_vehicle_type_id.blank?
-
end
-
-
#---------------------------------------------------
-
# Asset Properties
-
#---------------------------------------------------
-
-
def manufacturer_conditions
-
["assets.manufacturer_id = ?", manufacturer_id] unless manufacturer_id.blank?
-
end
-
-
def manufacturer_model_conditions
-
["assets.manufacturer_model LIKE ?", "%#{manufacturer_model}%"] unless manufacturer_model.blank?
-
end
-
-
def district_type_conditions
-
["assets.district_type_id = ?", district_id] unless district_id.blank?
-
end
-
-
def asset_type_conditions
-
["assets.asset_type_id = ?", asset_type_id] unless asset_type_id.blank?
-
end
-
-
def asset_subtype_conditions
-
["assets.asset_subtype_id = ?", asset_subtype_id] unless asset_subtype_id.blank?
-
end
-
-
def asset_tag_conditions
-
["assets.asset_tag LIKE ?", "%#{asset_tag}%"] unless asset_tag.blank?
-
end
-
-
#---------------------------------------------------
-
# Asset Condition
-
#---------------------------------------------------
-
def asset_condition_type_conditions
-
["assets.reported_condition_type_id = ?", condition_type_id] unless condition_type_id.blank?
-
end
-
-
def asset_replacement_year_conditions
-
["assets.estimated_replacement_year = ?", replacement_year] unless replacement_year.blank?
-
end
-
-
def keyword_conditions
-
#["products.name LIKE ?", "%#{keywords}%"] unless keywords.blank?
-
end
-
-
def minimum_price_conditions
-
#["products.price >= ?", minimum_price] unless minimum_price.blank?
-
end
-
-
def maximum_price_conditions
-
#["products.price <= ?", maximum_price] unless maximum_price.blank?
-
end
-
-
def category_conditions
-
#["products.category_id = ?", category_id] unless category_id.blank?
-
end
-
-
end
-
#------------------------------------------------------------------------------
-
#
-
# AssetDispositionService
-
#
-
# Contains business logic associated with managing the disposition of assets
-
#
-
#
-
#------------------------------------------------------------------------------
-
1
class AssetDispositionService
-
-
# Include the fiscal year mixin
-
1
include FiscalYear
-
-
#------------------------------------------------------------------------------
-
#
-
# Disposition List
-
#
-
# returns a list of assets that need to be disposed by FY, type, and subtype
-
#
-
#------------------------------------------------------------------------------
-
-
1
def disposition_list(org_id_list, fy_year=current_planning_year_year, asset_type_id=nil, asset_subtype_id=nil)
-
-
2
Rails.logger.debug "AssetDispositionService: disposition_list()"
-
#
-
2
if org_id_list.blank?
-
Rails.logger.warn "AssetDispositionService: disposition list: Org ID cannot be null"
-
return []
-
end
-
-
# Start to set up the query
-
2
conditions = []
-
2
values = []
-
-
# Filter for the selected org
-
2
conditions << "organization_id IN (?)"
-
2
values << org_id_list
-
-
# Can't already be marked as disposed
-
2
conditions << "disposition_date IS NULL"
-
-
# Scheduled replacement year, defaults to the next planning year unless
-
# specified
-
2
conditions << "scheduled_replacement_year = ?"
-
2
values << fy_year
-
-
# Limit by asset type
-
2
unless asset_type_id.nil?
-
2
conditions << "asset_type_id = ?"
-
2
values << asset_type_id
-
end
-
-
2
unless asset_subtype_id.nil?
-
2
conditions << "asset_subtype_id = ?"
-
2
values << asset_subtype_id
-
end
-
-
2
Asset.where(conditions.join(' AND '), *values)
-
end
-
-
#------------------------------------------------------------------------------
-
#
-
# Protected Methods
-
#
-
#------------------------------------------------------------------------------
-
1
protected
-
-
#------------------------------------------------------------------------------
-
#
-
# Private Methods
-
#
-
#------------------------------------------------------------------------------
-
1
private
-
-
end
-
#------------------------------------------------------------------------------
-
#
-
# AssetEndOfServiceService
-
#
-
# Contains business logic associated with managing the disposition of assets
-
#
-
#
-
#------------------------------------------------------------------------------
-
1
class AssetEndOfServiceService
-
-
# Include the fiscal year mixin
-
1
include FiscalYear
-
-
#------------------------------------------------------------------------------
-
#
-
# Disposition List
-
#
-
# returns a list of assets that need to be disposed by FY, type, and subtype
-
#
-
#------------------------------------------------------------------------------
-
-
1
def list(org_id_list, fy_year=current_planning_year_year, asset_type_id=nil, asset_subtype_id=nil)
-
-
2
Rails.logger.debug "AssetEndOfServiceService: list()"
-
#
-
2
if org_id_list.blank?
-
Rails.logger.warn "AssetEndOfServiceService: disposition list: Org ID cannot be null"
-
return []
-
end
-
-
# Start to set up the query
-
2
conditions = []
-
2
values = []
-
-
# Filter for the selected org
-
2
conditions << "organization_id IN (?)"
-
2
values << org_id_list
-
# Scheduled replacement year, defaults to the next planning year unless
-
# specified
-
2
conditions << "scheduled_replacement_year = ?"
-
2
values << fy_year
-
-
# Limit by asset type
-
#unless asset_type_id.blank?
-
# conditions << "asset_subtype_id = ?"
-
# values << asset_type_id
-
#end
-
-
2
unless asset_subtype_id.blank?
-
2
conditions << "asset_subtype_id = ?"
-
2
values << asset_subtype_id
-
end
-
-
2
Rails.application.config.asset_base_class_name.constantize.operational.where(conditions.join(' AND '), *values)
-
end
-
-
#------------------------------------------------------------------------------
-
#
-
# Protected Methods
-
#
-
#------------------------------------------------------------------------------
-
1
protected
-
-
#------------------------------------------------------------------------------
-
#
-
# Private Methods
-
#
-
#------------------------------------------------------------------------------
-
1
private
-
-
end
-
# The BootstrapBreadcrumbsBuilder is a Bootstrap compatible breadcrumb builder.
-
# It provides basic functionalities to render a breadcrumb navigation according to Bootstrap's conventions.
-
#
-
# BootstrapBreadcrumbsBuilder accepts a limited set of options:
-
# * separator: what should be displayed as a separator between elements
-
#
-
# You can use it with the :builder option on render_breadcrumbs:
-
# <%= render_breadcrumbs :builder => ::BootstrapBreadcrumbsBuilder, :separator => "»" %>
-
#
-
# Note: You may need to adjust the autoload_paths in your config/application.rb file for rails to load this class:
-
# config.autoload_paths += Dir["#{config.root}/lib/"]
-
#
-
class BootstrapBreadcrumbsBuilder < BreadcrumbsOnRails::Breadcrumbs::Builder
-
NoBreadcrumbsPassed = Class.new(StandardError)
-
-
def render
-
regular_elements = @elements.dup
-
active_element = regular_elements.pop #|| raise(NoBreadcrumbsPassed)
-
-
regular_elements.collect do |element|
-
render_regular_element(element)
-
end.join.html_safe + render_active_element(active_element).html_safe
-
-
end
-
-
def render_regular_element(element)
-
@context.content_tag :li do
-
@context.link_to(compute_name(element), compute_path(element), element.options)
-
end
-
end
-
-
def render_active_element(element)
-
@context.content_tag :li, class: 'active' do
-
compute_name(element) unless element.blank?
-
end
-
end
-
end
-
#------------------------------------------------------------------------------
-
1
class MessageTemplateMessageGenerator
-
-
1
def generate(message_template, custom_fields)
-
-
8
split_msg = message_template.body.split /([{}])/
-
-
8
msg= []
-
8
split_msg.each_with_index do |msg_part,idx|
-
75
msg << msg_part unless ['{', '}'].include?(msg_part) || split_msg[idx-1] == '{'
-
end
-
-
-
8
generated_msg = ''
-
-
8
(0..([msg.length, custom_fields.length].max)-1).each do |idx|
-
24
if msg[0].first == '{'
-
generated_msg << custom_fields[idx].to_s unless custom_fields[idx].blank?
-
generated_msg << msg[idx].to_s unless msg[idx].blank?
-
else
-
24
generated_msg << msg[idx].to_s unless msg[idx].blank?
-
24
generated_msg << custom_fields[idx].to_s unless custom_fields[idx].blank?
-
end
-
end
-
-
8
return generated_msg
-
end
-
end
-
#------------------------------------------------------------------------------
-
#
-
# NewUserService
-
#
-
# Contains business logic associated with creating new users
-
#
-
#------------------------------------------------------------------------------
-
-
1
class NewUserService
-
-
1
def build(form_params)
-
-
2
user = User.new(form_params)
-
# Set up a default password for them
-
2
user.password = SecureRandom.base64(8)
-
# Activate the account immediately
-
2
user.active = true
-
# Override opt-in for email notifications
-
2
user.notify_via_email = true
-
-
2
return user
-
end
-
-
# Steps to take if the user was valid
-
1
def post_process(user, assume_user_exists=false)
-
-
3
user.update_user_organization_filters unless Rails.application.config.try(:user_organization_filters_ignored).present?
-
-
3
user.viewable_organizations = user.user_organization_filter.try(:get_organizations) || user.organizations
-
3
user.save!
-
-
3
unless assume_user_exists
-
-
1
system_user = User.where(first_name: 'system', last_name: 'user').first
-
1
message_template = MessageTemplate.find_by(name: 'User1')
-
1
message_body = MessageTemplateMessageGenerator.new.generate(message_template,[user.email, Rails.application.routes.url_helpers.new_user_password_url])
-
-
-
1
msg = Message.new
-
1
msg.user = system_user
-
1
msg.organization = system_user.organization
-
1
msg.to_user = user
-
1
msg.subject = message_template.subject
-
1
msg.body = message_body
-
1
msg.priority_type = message_template.priority_type
-
1
msg.message_template = message_template
-
1
msg.active = message_template.active
-
1
msg.save
-
-
1
UserMailer.send_email_on_user_creation(user).deliver
-
end
-
-
end
-
end
-
1
class PutMetricDataService
-
-
1
def initialize
-
@cw = Fog::AWS::CloudWatch.new({:aws_secret_access_key => ENV['AWS_SECRET_KEY'],
-
:aws_access_key_id => ENV['AWS_ACCESS_KEY']
-
47
}) unless ENV['AWS_SECRET_KEY'].blank? && ENV['AWS_ACCESS_KEY'].blank?
-
47
@env = ENV['RAILS_ENV']
-
47
@namespace = "#{Rails.application.class.parent}:#{@env}"
-
-
end
-
-
1
def put_metric(name, unit, value, dimensions=[])
-
#puts [{'MetricName' => name, 'Unit' => unit, 'Value' => value, 'Dimensions' => dimensions}.select { |k, v| v!=[] }]
-
47
put_metrics_prepared([{'MetricName' => name, 'Unit' => unit, 'Value' => value, 'Dimensions' => dimensions}.select { |k, v| v!=[] }]) if @cw
-
end
-
-
1
def put_metrics_prepared metrics
-
metrics.each_slice(20).each do |slice|
-
while true
-
begin
-
log "Sent #{slice.size} prepared metrics to CW for namespace #{@namespace} #{slice.collect{|s| s['MetricName']}.sort.uniq.join(',')}"
-
debug slice.inspect
-
@cw.put_metric_data(@namespace, slice)
-
break
-
rescue Exception => e
-
# if e.response.body =~ %r{<Message>Rate exceeded</Message>}
-
# sleep 1
-
# else
-
log "Exception: #{e}"
-
log_exception_to_cw
-
raise e
-
# end
-
end
-
end
-
end
-
end
-
-
1
def log_exception_to_cw
-
@cw.put_metric_data(@namespace, [{'MetricName' => 'MonitoringException', 'Unit' => 'None', 'Value' => 1}]) unless @env=='development'
-
end
-
-
1
def log text
-
puts format_log_message(text)
-
end
-
-
1
def debug text
-
puts(format_log_message(text)) if ENV['DEBUG']
-
end
-
-
1
def format_log_message(text)
-
"#{Time.now.iso8601} (#{Process.pid}) #{text}"
-
end
-
-
end
-
#-------------------------------------------------------------------------------
-
#
-
# UserRoleService
-
#
-
# Contains business logic associated with managing user roles after an update
-
# or create event for a new user
-
#
-
#-------------------------------------------------------------------------------
-
1
class UserRoleService
-
-
#-----------------------------------------------------------------------------
-
# Returns the set of roles a user can assign to another user
-
#-----------------------------------------------------------------------------
-
1
def assignable_roles user
-
3
Role.roles.where('weight <= ?', user.primary_role.weight)
-
end
-
#-----------------------------------------------------------------------------
-
# Returns the set of roles a user can assign to another user. Only admins can
-
# assign an admin privilege
-
#-----------------------------------------------------------------------------
-
1
def assignable_privileges user
-
2
if user.has_role? :admin
-
1
Role.privileges
-
else
-
1
Role.privileges.where('name <> ?', "admin")
-
end
-
end
-
#-----------------------------------------------------------------------------
-
# Override this method to invoke business logic for managing roles. Params
-
# are:
-
# user - the user being updated
-
# manager - the user doing the updating (usually current_user)
-
# role_id - the role_id that the user should have
-
# privilege_ids - an array of privilege ids that the user should have
-
#-----------------------------------------------------------------------------
-
1
def set_roles_and_privileges(user, manager, role_id, privilege_ids)
-
-
7
Rails.logger.debug "Assign roles and privileges: user = #{user}, manager = #{manager}, role_id = #{role_id}, privilege_ids = #{privilege_ids}"
-
7
return if user.blank?
-
-
# Check all the roles and privileges and revoke/assign as needed
-
7
Role.all.each do |role|
-
56
Rails.logger.debug "Checking role #{role}, id = #{role.id}"
-
56
if role_id == role.id.to_s
-
# Its the role they are assigned
-
5
assign_role user, role, manager
-
-
51
elsif privilege_ids.present? && privilege_ids.include?(role.id.to_s)
-
# Its a privilege they should be assigned
-
1
assign_role user, role, manager
-
-
else
-
# otherwise revoke it if it exists
-
50
revoke_role user, role, manager
-
end
-
end
-
-
# Make sure the user has the user role and assign it if they dont (except for guests)
-
7
if (!(user.has_role? :guest) && !(user.has_role? :user))
-
5
assign_role user, Role.find_by(:name => 'user'), manager
-
end
-
end
-
-
#-----------------------------------------------------------------------------
-
# Override this method to invoke any business logic for post processing after
-
# a user has been created and saved. For example, sending emails, configuring
-
# accounts, etc.
-
#-----------------------------------------------------------------------------
-
1
def post_process(user)
-
# No actions
-
end
-
-
#-----------------------------------------------------------------------------
-
# Assigns a single role or privilege to a user
-
#-----------------------------------------------------------------------------
-
1
def assign_role user, role, manager
-
-
12
Rails.logger.debug "Assigning role #{role} for user #{user}"
-
-
12
begin
-
12
users_role = UsersRole.find_or_create_by(:user => user, :role => role) do |r|
-
11
r.granted_by_user = manager
-
11
r.granted_on_date = Date.today
-
11
r.active = true
-
end
-
rescue ActiveRecord::RecordNotUnique
-
retry
-
end
-
end
-
#-----------------------------------------------------------------------------
-
# Revokes a single role or privilege from a user
-
#-----------------------------------------------------------------------------
-
1
def revoke_role user, role, manager
-
# must search by WHERE because no primary key ID for .destroy. Use .delete_all
-
52
users_role = UsersRole.where(:user => user, :role => role)
-
52
if users_role.present?
-
7
Rails.logger.debug "Revoking role #{role} for user #{user}. #{users_role.inspect}"
-
7
users_role.delete_all
-
#user.remove_role role.name
-
# users_role.active = false
-
# users_role.revoked_by_user = manager
-
# users_role.revoked_on_date = Date.today
-
# users_role.save
-
end
-
-
end
-
-
end
-
class CarrierStringIO < StringIO
-
attr_accessor :original_filename
-
attr_accessor :content_type
-
end
-
1
class DocumentUploader < TransamAbstractUploader
-
-
1
def store_dir
-
40
"document_uploads/#{model.object_key}"
-
end
-
-
# A white list of extensions which are allowed to be uploaded.
-
1
def extension_white_list
-
%w( pdf doc docx xls xlsx ppt )
-
end
-
-
end
-
1
class ExcelUploader < TransamAbstractUploader
-
-
1
def store_dir
-
31
"file_uploads/#{model.object_key}"
-
end
-
-
# Add a white list of extensions which are allowed to be uploaded.
-
# For images you might use something like this:
-
1
def extension_white_list
-
%w(xlsx)
-
end
-
-
end
-
1
class ImageUploader < TransamAbstractUploader
-
-
1
include CarrierWave::RMagick
-
-
1
version :thumb do
-
1
process :resize_to_fill => [145, 100]
-
end
-
-
1
def store_dir
-
48
"image_uploads/#{model.object_key}"
-
end
-
-
# Add a white list of extensions which are allowed to be uploaded.
-
# For images you might use something like this:
-
1
def extension_white_list
-
1
%w(png jpg jpeg gif)
-
end
-
-
end
-
1
class TransamAbstractUploader < CarrierWave::Uploader::Base
-
-
# make sure to save the orignal filename
-
1
before :cache, :save_original_filename
-
-
# Move files from cache to store rather than re-uploading
-
1
def move_to_cache
-
80
true
-
end
-
-
1
def move_to_store
-
194
true
-
end
-
-
# Override the filename of the uploaded files:
-
# Avoid using model.id or version_name here, see uploader/store.rb for details.
-
1
def filename
-
188
"#{secure_token}.#{file.extension}" if original_filename.present?
-
end
-
-
1
def save_original_filename(file)
-
98
model.original_filename ||= file.original_filename if file.respond_to?(:original_filename)
-
end
-
-
1
protected
-
-
1
def secure_token
-
188
var = :"@#{mounted_as}_secure_token"
-
188
model.instance_variable_get(var) or model.instance_variable_set(var, SecureRandom.uuid)
-
end
-
-
end
-
# lib/file_size_validator.rb
-
# Based on: https://gist.github.com/795665
-
-
1
class FileSizeValidator < ActiveModel::EachValidator
-
1
MESSAGES = { :is => :wrong_size, :minimum => :size_too_small, :maximum => :size_too_big }.freeze
-
1
CHECKS = { :is => :==, :minimum => :>=, :maximum => :<= }.freeze
-
-
1
DEFAULT_TOKENIZER = lambda { |value| value.split(//) }
-
1
RESERVED_OPTIONS = [:minimum, :maximum, :within, :is, :tokenizer, :too_short, :too_long]
-
-
1
def initialize(options)
-
4
if range = (options.delete(:in) || options.delete(:within))
-
raise ArgumentError, ":in and :within must be a Range" unless range.is_a?(Range)
-
options[:minimum], options[:maximum] = range.begin, range.end
-
options[:maximum] -= 1 if range.exclude_end?
-
end
-
-
4
super
-
end
-
-
1
def check_validity!
-
4
keys = CHECKS.keys & options.keys
-
-
4
if keys.empty?
-
raise ArgumentError, 'Range unspecified. Specify the :within, :maximum, :minimum, or :is option.'
-
end
-
-
4
keys.each do |key|
-
4
value = options[key]
-
-
4
unless value.is_a?(Integer) && value >= 0
-
raise ArgumentError, ":#{key} must be a nonnegative Integer"
-
end
-
end
-
end
-
-
1
def validate_each(record, attribute, value)
-
122
raise(ArgumentError, "A CarrierWave::Uploader::Base object was expected") unless value.kind_of? CarrierWave::Uploader::Base
-
-
122
value = (options[:tokenizer] || DEFAULT_TOKENIZER).call(value) if value.kind_of?(String)
-
-
122
CHECKS.each do |key, validity_check|
-
366
next unless check_value = options[key]
-
-
122
value ||= [] if key == :maximum
-
-
122
value_size = value.size
-
122
next if value_size.send(validity_check, check_value)
-
-
errors_options = options.except(*RESERVED_OPTIONS)
-
errors_options[:file_size] = help.number_to_human_size check_value
-
-
default_message = options[MESSAGES[key]]
-
errors_options[:message] ||= default_message if default_message
-
-
record.errors.add(attribute, MESSAGES[key], errors_options)
-
end
-
end
-
-
1
def help
-
Helper.instance
-
end
-
-
1
class Helper
-
1
include Singleton
-
1
include ActionView::Helpers::NumberHelper
-
end
-
end
-
1
require "transam_core/engine"
-
-
1
module TransamCore
-
-
end
-
1
require 'active_record/acts_as'
-
1
require 'devise'
-
1
require 'rolify'
-
1
require 'cancan'
-
1
require 'unitwise'
-
1
require 'chronic'
-
1
require 'breadcrumbs_on_rails'
-
1
require 'state_machines'
-
1
require 'state_machines-activemodel'
-
1
require 'state_machines-activerecord'
-
1
require 'delayed_job'
-
1
require 'delayed_job_active_record'
-
1
require 'high_voltage'
-
1
require 'haml-rails'
-
1
require 'simple_form'
-
1
require 'country_select'
-
1
require 'gritter'
-
1
require 'fog'
-
1
require 'carrierwave'
-
1
require 'rmagick'
-
1
require 'countries'
-
1
require 'rails-data-migrations'
-
1
require 'font-awesome-sass'
-
1
require 'bootstrap-datepicker-rails'
-
1
require 'bootstrap-editable-rails'
-
1
require 'bootstrap-sass'
-
1
require 'wicked'
-
1
require 'cocoon'
-
1
require 'jquery-ui-rails'
-
1
require 'jquery-form-rails'
-
1
require 'roo-xls'
-
1
require 'kaminari'
-
-
1
require 'paper_trail'
-
1
require 'paper_trail-globalid'
-
-
1
require 'deep_cloneable'
-
-
# API
-
#require 'open_api'
-
1
require 'jbuilder'
-
1
require 'responders'
-
1
require 'simple_token_authentication'
-
1
require 'api-pagination'
-
-
1
module TransamCore
-
1
class Engine < ::Rails::Engine
-
# Add a load path for this specific Engine
-
1
config.autoload_paths += %W(#{Rails.root}/app/calculators)
-
1
config.autoload_paths += %W(#{Rails.root}/app/file_handlers)
-
1
config.autoload_paths += %W(#{Rails.root}/app/jobs)
-
1
config.autoload_paths += %W(#{Rails.root}/app/reports)
-
1
config.autoload_paths += %W(#{Rails.root}/app/searches)
-
1
config.autoload_paths += %W(#{Rails.root}/app/services)
-
1
config.autoload_paths += %W(#{Rails.root}/app/uploaders)
-
-
# Append migrations from the engine into the main app
-
1
initializer :append_migrations do |app|
-
1
unless app.root.to_s.match root.to_s
-
config.paths["db/migrate"].expanded.each do |expanded_path|
-
app.config.paths["db/migrate"] << expanded_path
-
end
-
app.config.paths.add "db/data_migrations"
-
config.paths.add "db/data_migrations"
-
config.paths["db/data_migrations"].expanded.each do |expanded_path|
-
app.config.paths["db/data_migrations"] << expanded_path
-
end
-
end
-
end
-
-
1
config.generators do |g|
-
1
g.test_framework :rspec, :fixture => false
-
1
g.fixture_replacement :factory_bot, :dir => 'spec/factories'
-
1
g.assets false
-
1
g.helper false
-
end
-
end
-
end
-
module TransamCore
-
VERSION = "2.7.1"
-
end